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/12 00:22:11 UTC
[commons-numbers] branch master updated: [NUMBERS-139] Implement
add and subtract for imaginary only.
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
The following commit(s) were added to refs/heads/master by this push:
new d96bc97 [NUMBERS-139] Implement add and subtract for imaginary only.
d96bc97 is described below
commit d96bc97cfdca5b36d3f8b2561f60c7cec72761fe
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Thu Dec 12 00:22:07 2019 +0000
[NUMBERS-139] Implement add and subtract for imaginary only.
addImaginary(double)
subtractImaginary(double)
subtractFrom(double)
subtractFromImaginary(double)
---
.../apache/commons/numbers/complex/Complex.java | 126 ++++++++-
.../commons/numbers/complex/ComplexTest.java | 303 ++++++++++++++++++---
2 files changed, 379 insertions(+), 50 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 0181189..f3ac498 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
@@ -332,11 +332,10 @@ public final class Complex implements Serializable {
}
/**
- * Returns a {@code Complex} whose value is
- * {@code (this + addend)}.
+ * Returns a {@code Complex} whose value is {@code (this + addend)}.
* Implements the formula:
* <pre>
- * (a + b i) + (c + d i) = (a + c) + i (b + d)
+ * (a + i b) + (c + i d) = (a + c) + i (b + d)
* </pre>
*
* @param addend Value to be added to this {@code Complex}.
@@ -351,16 +350,54 @@ public final class Complex implements Serializable {
/**
* Returns a {@code Complex} whose value is {@code (this + addend)},
* with {@code addend} interpreted as a real number.
+ * Implements the formula:
+ * <pre>
+ * (a + i b) + c = (a + c) + i b
+ * </pre>
+ *
+ * <p>This method is included for compatibility with ISO C99 which defines arithmetic between
+ * real-only and complex numbers.</p>
+ *
+ * <p>Note: This method preserves the sign of the imaginary component {@code b} if it is {@code -0.0}.
+ * The sign would be lost if adding {@code (c + i 0)} using
+ * {@link #add(Complex) add(Complex.ofReal(addend))} since
+ * {@code -0.0 + 0.0 = 0.0}.
*
* @param addend Value to be added to this {@code Complex}.
* @return {@code this + addend}.
* @see #add(Complex)
+ * @see #ofReal(double)
*/
public Complex add(double addend) {
return new Complex(real + addend, imaginary);
}
/**
+ * Returns a {@code Complex} whose value is {@code (this + addend)},
+ * with {@code addend} interpreted as an imaginary number.
+ * Implements the formula:
+ * <pre>
+ * (a + i b) + i d = a + i (b + d)
+ * </pre>
+ *
+ * <p>This method is included for compatibility with ISO C99 which defines arithmetic between
+ * imaginary-only and complex numbers.</p>
+ *
+ * <p>Note: This method preserves the sign of the real component {@code a} if it is {@code -0.0}.
+ * The sign would be lost if adding {@code (0 + i d)} using
+ * {@link #add(Complex) add(Complex.ofCartesian(0, addend))} since
+ * {@code -0.0 + 0.0 = 0.0}.
+ *
+ * @param addend Value to be added to this {@code Complex}.
+ * @return {@code this + addend}.
+ * @see #add(Complex)
+ * @see #ofCartesian(double, double)
+ */
+ public Complex addImaginary(double addend) {
+ return new Complex(real, imaginary + addend);
+ }
+
+ /**
* Returns the
* <a href="http://mathworld.wolfram.com/ComplexConjugate.html">conjugate</a>
* z̅ of this complex number z.
@@ -926,11 +963,10 @@ public final class Complex implements Serializable {
}
/**
- * Returns a {@code Complex} whose value is
- * {@code (this - subtrahend)}.
+ * Returns a {@code Complex} whose value is {@code (this - subtrahend)}.
* Implements the formula:
* <pre>
- * (a + b i) - (c + d i) = (a - c) + i (b - d)
+ * (a + i b) - (c + i d) = (a - c) + i (b - d)
* </pre>
*
* @param subtrahend value to be subtracted from this {@code Complex}.
@@ -943,13 +979,16 @@ public final class Complex implements Serializable {
}
/**
- * Returns a {@code Complex} whose value is
- * {@code (this - subtrahend)}.
+ * Returns a {@code Complex} whose value is {@code (this - subtrahend)},
+ * with {@code subtrahend} interpreted as a real number.
* Implements the formula:
* <pre>
- * (a + b i) - c = (a - c) + b i
+ * (a + i b) - c = (a - c) + i b
* </pre>
*
+ * <p>This method is included for compatibility with ISO C99 which defines arithmetic between
+ * real-only and complex numbers.</p>
+ *
* @param subtrahend value to be subtracted from this {@code Complex}.
* @return {@code this - subtrahend}.
* @see #subtract(Complex)
@@ -959,6 +998,75 @@ public final class Complex implements Serializable {
}
/**
+ * Returns a {@code Complex} whose value is {@code (this - subtrahend)},
+ * with {@code subtrahend} interpreted as an imaginary number.
+ * Implements the formula:
+ * <pre>
+ * (a + i b) - i d = a + i (b - d)
+ * </pre>
+ *
+ * <p>This method is included for compatibility with ISO C99 which defines arithmetic between
+ * imaginary-only and complex numbers.</p>
+ *
+ * @param subtrahend value to be subtracted from this {@code Complex}.
+ * @return {@code this - subtrahend}.
+ * @see #subtract(Complex)
+ */
+ public Complex subtractImaginary(double subtrahend) {
+ return new Complex(real, imaginary - subtrahend);
+ }
+
+ /**
+ * Returns a {@code Complex} whose value is {@code (minuend - this)},
+ * with {@code minuend} interpreted as a real number.
+ * Implements the formula:
+ * <pre>
+ * c - (a + i b) = (c - a) - i b
+ * </pre>
+ *
+ * <p>This method is included for compatibility with ISO C99 which defines arithmetic between
+ * real-only and complex numbers.</p>
+ *
+ * <p>Note: This method inverts the sign of the imaginary component {@code b} if it is {@code 0.0}.
+ * The sign would not be inverted if subtracting from {@code (c + i 0)} using
+ * {@link #subtract(Complex) Complex.ofReal(minuend).subtract(this))} since
+ * {@code 0.0 - 0.0 = 0.0}.
+ *
+ * @param minuend value this {@code Complex} is to be subtracted from.
+ * @return {@code minuend - this}.
+ * @see #subtract(Complex)
+ * @see #ofReal(double)
+ */
+ public Complex subtractFrom(double minuend) {
+ return new Complex(minuend - real, -imaginary);
+ }
+
+ /**
+ * Returns a {@code Complex} whose value is {@code (this - subtrahend)},
+ * with {@code minuend} interpreted as an imaginary number.
+ * Implements the formula:
+ * <pre>
+ * i d - (a + i b) = -a + i (d - b)
+ * </pre>
+ *
+ * <p>This method is included for compatibility with ISO C99 which defines arithmetic between
+ * imaginary-only and complex numbers.</p>
+ *
+ * <p>Note: This method inverts the sign of the real component {@code a} if it is {@code 0.0}.
+ * The sign would not be inverted if subtracting from {@code (0 + i d)} using
+ * {@link #subtract(Complex) Complex.ofCartesian(0, minuend).subtract(this))} since
+ * {@code 0.0 - 0.0 = 0.0}.
+ *
+ * @param minuend value this {@code Complex} is to be subtracted from.
+ * @return {@code this - subtrahend}.
+ * @see #subtract(Complex)
+ * @see #ofCartesian(double, double)
+ */
+ public Complex subtractFromImaginary(double minuend) {
+ return new Complex(-real, minuend - imaginary);
+ }
+
+ /**
* Compute the
* <a href="http://mathworld.wolfram.com/InverseCosine.html">
* inverse cosine</a> of this complex number.
diff --git a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java
index 4647052..217588b 100644
--- a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java
+++ b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java
@@ -67,6 +67,16 @@ public class ComplexTest {
FINITE
}
+ /**
+ * Convenience constructor for testing using only and imaginary component.
+ *
+ * @param imaginary the imaginary part
+ * @return the complex
+ */
+ private static Complex ofImaginary(double imaginary) {
+ return Complex.ofCartesian(0, imaginary);
+ }
+
@Test
public void testCartesianConstructor() {
final Complex z = Complex.ofCartesian(3.0, 4.0);
@@ -209,40 +219,99 @@ public class ComplexTest {
Complex x = Complex.ofCartesian(1, 1);
final Complex z = Complex.ofCartesian(inf, 0);
final Complex w = x.add(z);
- Assertions.assertEquals(1, w.getImaginary(), 0);
- Assertions.assertEquals(inf, w.getReal(), 0);
+ Assertions.assertEquals(1, w.getImaginary());
+ Assertions.assertEquals(inf, w.getReal());
x = Complex.ofCartesian(neginf, 0);
Assertions.assertTrue(Double.isNaN(x.add(z).getReal()));
}
+ @Test
+ public void testAddReal() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = 5.0;
+ Complex z = x.add(y);
+ Assertions.assertEquals(8.0, z.getReal());
+ Assertions.assertEquals(4.0, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, x.add(Complex.ofReal(y)));
+ }
@Test
- public void testScalarAdd() {
+ public void testAddRealNaN() {
final Complex x = Complex.ofCartesian(3.0, 4.0);
- final double yDouble = 2.0;
- final Complex yComplex = Complex.ofReal(yDouble);
- Assertions.assertEquals(x.add(yComplex), x.add(yDouble));
+ final double y = nan;
+ Complex z = x.add(y);
+ Assertions.assertEquals(nan, z.getReal());
+ Assertions.assertEquals(4.0, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, x.add(Complex.ofReal(y)));
}
@Test
- public void testScalarAddNaN() {
+ public void testAddRealInf() {
final Complex x = Complex.ofCartesian(3.0, 4.0);
- final double yDouble = Double.NaN;
- final Complex yComplex = Complex.ofReal(yDouble);
- Assertions.assertEquals(x.add(yComplex), x.add(yDouble));
+ final double y = inf;
+ Complex z = x.add(y);
+ Assertions.assertEquals(inf, z.getReal());
+ Assertions.assertEquals(4.0, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, x.add(Complex.ofReal(y)));
}
@Test
- public void testScalarAddInf() {
- Complex x = Complex.ofCartesian(1, 1);
- final double yDouble = Double.POSITIVE_INFINITY;
+ public void testAddRealWithNegZeroImaginary() {
+ final Complex x = Complex.ofCartesian(3.0, -0.0);
+ final double y = 5.0;
+ Complex z = x.add(y);
+ Assertions.assertEquals(8.0, z.getReal());
+ Assertions.assertEquals(-0.0, z.getImaginary(), "Expected sign preservation");
+ // Sign-preservation is a problem: -0.0 + 0.0 == 0.0
+ Assertions.assertNotEquals(z, x.add(Complex.ofReal(y)));
+ }
- final Complex yComplex = Complex.ofReal(yDouble);
- Assertions.assertEquals(x.add(yComplex), x.add(yDouble));
+ @Test
+ public void testAddImaginary() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = 5.0;
+ Complex z = x.addImaginary(y);
+ Assertions.assertEquals(3.0, z.getReal());
+ Assertions.assertEquals(9.0, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, x.add(ofImaginary(y)));
+ }
- x = Complex.ofCartesian(neginf, 0);
- Assertions.assertEquals(x.add(yComplex), x.add(yDouble));
+ @Test
+ public void testAddImaginaryNaN() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = nan;
+ Complex z = x.addImaginary(y);
+ Assertions.assertEquals(3.0, z.getReal());
+ Assertions.assertEquals(nan, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, x.add(ofImaginary(y)));
+ }
+
+ @Test
+ public void testAddImaginaryInf() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = inf;
+ Complex z = x.addImaginary(y);
+ Assertions.assertEquals(3.0, z.getReal());
+ Assertions.assertEquals(inf, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, x.add(ofImaginary(y)));
+ }
+
+ @Test
+ public void testAddImaginaryWithNegZeroReal() {
+ final Complex x = Complex.ofCartesian(-0.0, 4.0);
+ final double y = 5.0;
+ Complex z = x.addImaginary(y);
+ Assertions.assertEquals(-0.0, z.getReal());
+ Assertions.assertEquals(9.0, z.getImaginary(), "Expected sign preservation");
+ // Sign-preservation is a problem: -0.0 + 0.0 == 0.0
+ Assertions.assertNotEquals(z, x.add(ofImaginary(y)));
}
@Test
@@ -458,49 +527,201 @@ public class ComplexTest {
@Test
public void testSubtract() {
final Complex x = Complex.ofCartesian(3.0, 4.0);
- final Complex y = Complex.ofCartesian(5.0, 6.0);
+ final Complex y = Complex.ofCartesian(5.0, 7.0);
final Complex z = x.subtract(y);
Assertions.assertEquals(-2.0, z.getReal());
- Assertions.assertEquals(-2.0, z.getImaginary());
+ Assertions.assertEquals(-3.0, z.getImaginary());
}
@Test
public void testSubtractInf() {
- Complex x = Complex.ofCartesian(1, 1);
- final Complex z = Complex.ofCartesian(neginf, 0);
- final Complex w = x.subtract(z);
- Assertions.assertEquals(1, w.getImaginary(), 0);
- Assertions.assertEquals(inf, w.getReal(), 0);
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final Complex y = Complex.ofCartesian(inf, 7.0);
+ Complex z = x.subtract(y);
+ Assertions.assertEquals(neginf, z.getReal());
+ Assertions.assertEquals(-3.0, z.getImaginary());
- x = Complex.ofCartesian(neginf, 0);
- Assertions.assertTrue(Double.isNaN(x.subtract(z).getReal()));
+ z = y.subtract(y);
+ Assertions.assertEquals(nan, z.getReal());
+ Assertions.assertEquals(0.0, z.getImaginary());
}
@Test
- public void testScalarSubtract() {
+ public void testSubtractReal() {
final Complex x = Complex.ofCartesian(3.0, 4.0);
- final double yDouble = 2.0;
- final Complex yComplex = Complex.ofReal(yDouble);
- Assertions.assertEquals(x.subtract(yComplex), x.subtract(yDouble));
+ final double y = 5.0;
+ Complex z = x.subtract(y);
+ Assertions.assertEquals(-2.0, z.getReal());
+ Assertions.assertEquals(4.0, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, x.subtract(Complex.ofReal(y)));
}
@Test
- public void testScalarSubtractNaN() {
+ public void testSubtractRealNaN() {
final Complex x = Complex.ofCartesian(3.0, 4.0);
- final double yDouble = Double.NaN;
- final Complex yComplex = Complex.ofReal(yDouble);
- Assertions.assertEquals(x.subtract(yComplex), x.subtract(yDouble));
+ final double y = nan;
+ Complex z = x.subtract(y);
+ Assertions.assertEquals(nan, z.getReal());
+ Assertions.assertEquals(4.0, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, x.subtract(Complex.ofReal(y)));
}
@Test
- public void testScalarSubtractInf() {
- Complex x = Complex.ofCartesian(1, 1);
- final double yDouble = Double.POSITIVE_INFINITY;
- final Complex yComplex = Complex.ofReal(yDouble);
- Assertions.assertEquals(x.subtract(yComplex), x.subtract(yDouble));
+ public void testSubtractRealInf() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = inf;
+ Complex z = x.subtract(y);
+ Assertions.assertEquals(-inf, z.getReal());
+ Assertions.assertEquals(4.0, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, x.subtract(Complex.ofReal(y)));
+ }
- x = Complex.ofCartesian(neginf, 0);
- Assertions.assertEquals(x.subtract(yComplex), x.subtract(yDouble));
+ @Test
+ public void testSubtractRealWithNegZeroImaginary() {
+ final Complex x = Complex.ofCartesian(3.0, -0.0);
+ final double y = 5.0;
+ Complex z = x.subtract(y);
+ Assertions.assertEquals(-2.0, z.getReal());
+ Assertions.assertEquals(-0.0, z.getImaginary());
+ // Equivalent
+ // Sign-preservation is not a problem: -0.0 - 0.0 == -0.0
+ Assertions.assertEquals(z, x.subtract(Complex.ofReal(y)));
+ }
+
+ @Test
+ public void testSubtractImaginary() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = 5.0;
+ Complex z = x.subtractImaginary(y);
+ Assertions.assertEquals(3.0, z.getReal());
+ Assertions.assertEquals(-1.0, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, x.subtract(ofImaginary(y)));
+ }
+
+ @Test
+ public void testSubtractImaginaryNaN() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = nan;
+ Complex z = x.subtractImaginary(y);
+ Assertions.assertEquals(3.0, z.getReal());
+ Assertions.assertEquals(nan, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, x.subtract(ofImaginary(y)));
+ }
+
+ @Test
+ public void testSubtractImaginaryInf() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = inf;
+ Complex z = x.subtractImaginary(y);
+ Assertions.assertEquals(3.0, z.getReal());
+ Assertions.assertEquals(-inf, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, x.subtract(ofImaginary(y)));
+ }
+
+ @Test
+ public void testSubtractImaginaryWithNegZeroReal() {
+ final Complex x = Complex.ofCartesian(-0.0, 4.0);
+ final double y = 5.0;
+ Complex z = x.subtractImaginary(y);
+ Assertions.assertEquals(-0.0, z.getReal());
+ Assertions.assertEquals(-1.0, z.getImaginary());
+ // Equivalent
+ // Sign-preservation is not a problem: -0.0 - 0.0 == -0.0
+ Assertions.assertEquals(z, x.subtract(ofImaginary(y)));
+ }
+
+ @Test
+ public void testSubtractFromReal() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = 5.0;
+ Complex z = x.subtractFrom(y);
+ Assertions.assertEquals(2.0, z.getReal());
+ Assertions.assertEquals(-4.0, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, Complex.ofReal(y).subtract(x));
+ }
+
+ @Test
+ public void testSubtractFromRealNaN() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = nan;
+ Complex z = x.subtractFrom(y);
+ Assertions.assertEquals(nan, z.getReal());
+ Assertions.assertEquals(-4.0, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, Complex.ofReal(y).subtract(x));
+ }
+
+ @Test
+ public void testSubtractFromRealInf() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = inf;
+ Complex z = x.subtractFrom(y);
+ Assertions.assertEquals(inf, z.getReal());
+ Assertions.assertEquals(-4.0, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, Complex.ofReal(y).subtract(x));
+ }
+
+ @Test
+ public void testSubtractFromRealWithPosZeroImaginary() {
+ final Complex x = Complex.ofCartesian(3.0, 0.0);
+ final double y = 5.0;
+ Complex z = x.subtractFrom(y);
+ Assertions.assertEquals(2.0, z.getReal());
+ Assertions.assertEquals(-0.0, z.getImaginary(), "Expected sign inversion");
+ // Sign-inversion is a problem: 0.0 - 0.0 == 0.0
+ Assertions.assertNotEquals(z, Complex.ofReal(y).subtract(x));
+ }
+
+ @Test
+ public void testSubtractFromImaginary() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = 5.0;
+ Complex z = x.subtractFromImaginary(y);
+ Assertions.assertEquals(-3.0, z.getReal());
+ Assertions.assertEquals(1.0, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, ofImaginary(y).subtract(x));
+ }
+
+ @Test
+ public void testSubtractFromImaginaryNaN() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = nan;
+ Complex z = x.subtractFromImaginary(y);
+ Assertions.assertEquals(-3.0, z.getReal());
+ Assertions.assertEquals(nan, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, ofImaginary(y).subtract(x));
+ }
+
+ @Test
+ public void testSubtractFromImaginaryInf() {
+ final Complex x = Complex.ofCartesian(3.0, 4.0);
+ final double y = inf;
+ Complex z = x.subtractFromImaginary(y);
+ Assertions.assertEquals(-3.0, z.getReal());
+ Assertions.assertEquals(inf, z.getImaginary());
+ // Equivalent
+ Assertions.assertEquals(z, ofImaginary(y).subtract(x));
+ }
+
+ @Test
+ public void testSubtractFromImaginaryWithPosZeroReal() {
+ final Complex x = Complex.ofCartesian(0.0, 4.0);
+ final double y = 5.0;
+ Complex z = x.subtractFromImaginary(y);
+ Assertions.assertEquals(-0.0, z.getReal(), "Expected sign inversion");
+ Assertions.assertEquals(1.0, z.getImaginary());
+ // Sign-inversion is a problem: 0.0 - 0.0 == 0.0
+ Assertions.assertNotEquals(z, ofImaginary(y).subtract(x));
}
@Test