http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/40418955/commons-numbers-complex-streams/src/test/java/org/apache/commons/numbers/complex/streams/ComplexUtilsTest.java ---------------------------------------------------------------------- diff --git a/commons-numbers-complex-streams/src/test/java/org/apache/commons/numbers/complex/streams/ComplexUtilsTest.java b/commons-numbers-complex-streams/src/test/java/org/apache/commons/numbers/complex/streams/ComplexUtilsTest.java new file mode 100644 index 0000000..d348571 --- /dev/null +++ b/commons-numbers-complex-streams/src/test/java/org/apache/commons/numbers/complex/streams/ComplexUtilsTest.java @@ -0,0 +1,476 @@ +/* + * 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.streams; + +import org.apache.commons.numbers.complex.Complex; + +import org.junit.Assert; +import org.junit.Test; + +/** + */ +public class ComplexUtilsTest { + + private static final double inf = Double.POSITIVE_INFINITY; + private static final double negInf = Double.NEGATIVE_INFINITY; + private static final double nan = Double.NaN; + private static final double pi = Math.PI; + + private static final Complex negInfInf = Complex.ofCartesian(negInf, inf); + private static final Complex infNegInf = Complex.ofCartesian(inf, negInf); + private static final Complex infInf = Complex.ofCartesian(inf, inf); + private static final Complex negInfNegInf = Complex.ofCartesian(negInf, negInf); + private static final Complex infNaN = Complex.ofCartesian(inf, nan); + private static final Complex NAN = Complex.ofCartesian(nan, nan); + + private static Complex c[]; // complex array with real values even and imag + // values odd + private static Complex cr[]; // complex array with real values consecutive + private static Complex ci[]; // complex array with imag values consecutive + private static double d[]; // real array with consecutive vals + private static double di[]; // real array with consecutive vals, + // 'interleaved' length + private static float f[]; // real array with consecutive vals + private static float fi[]; // real array with consec vals, interleaved + // length + private static double sr[]; // real component of split array, evens + private static double si[]; // imag component of split array, odds + private static float sfr[]; // real component of split array, float, evens + private static float sfi[]; // imag component of split array, float, odds + static Complex ans1, ans2; // answers to single value extraction methods + static Complex[] ansArrayc1r, ansArrayc1i, ansArrayc2r, ansArrayc2i, ansArrayc3, ansArrayc4; // answers + // to + // range + // extraction + // methods + static double[] ansArrayd1r, ansArrayd2r, ansArrayd1i, ansArrayd2i, ansArraydi1, ansArraydi2; + static float[] ansArrayf1r, ansArrayf2r, ansArrayf1i, ansArrayf2i, ansArrayfi1, ansArrayfi2; + static String msg; // error message for AssertEquals + static Complex[][] c2d, cr2d, ci2d; // for 2d methods + static Complex[][][] c3d, cr3d, ci3d; // for 3d methods + static double[][] d2d, di2d, sr2d, si2d; + static double[][][] d3d, di3d, sr3d, si3d; + static float[][] f2d, fi2d, sfr2d, sfi2d; + static float[][][] f3d, fi3d, sfr3d, sfi3d; + + private static void setArrays() { // initial setup method + c = new Complex[10]; + cr = new Complex[10]; + ci = new Complex[10]; + d = new double[10]; + f = new float[10]; + di = new double[20]; + fi = new float[20]; + sr = new double[10]; + si = new double[10]; + sfr = new float[10]; + sfi = new float[10]; + c2d = new Complex[10][10]; + cr2d = new Complex[10][10]; + ci2d = new Complex[10][10]; + c3d = new Complex[10][10][10]; + cr3d = new Complex[10][10][10]; + ci3d = new Complex[10][10][10]; + d2d = new double[10][10]; + d3d = new double[10][10][10]; + f2d = new float[10][10]; + f3d = new float[10][10][10]; + sr2d = new double[10][10]; + sr3d = new double[10][10][10]; + si2d = new double[10][10]; + si3d = new double[10][10][10]; + sfr2d = new float[10][10]; + sfr3d = new float[10][10][10]; + sfi2d = new float[10][10]; + sfi3d = new float[10][10][10]; + di2d = new double[10][20]; + di3d = new double[10][10][20]; + fi2d = new float[10][20]; + fi3d = new float[10][10][20]; + for (int i = 0; i < 20; i += 2) { + d[i / 2] = i / 2; + f[i / 2] = i / 2; + di[i] = i; + di[i + 1] = i + 1; + fi[i] = i; + fi[i + 1] = i + 1; + c[i / 2] = Complex.ofCartesian(i, i + 1); + cr[i / 2] = Complex.ofReal(i / 2); + ci[i / 2] = Complex.ofCartesian(0, i / 2); + sr[i / 2] = i; + si[i / 2] = i + 1; + sfr[i / 2] = i; + sfi[i / 2] = i + 1; + } + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 20; j += 2) { + d2d[i][j / 2] = 10 * i + j / 2; + f2d[i][j / 2] = 10 * i + j / 2; + sr2d[i][j / 2] = 10 * i + j; + si2d[i][j / 2] = 10 * i + j + 1; + sfr2d[i][j / 2] = 10 * i + j; + sfi2d[i][j / 2] = 10 * i + j + 1; + di2d[i][j] = 10 * i + j; + di2d[i][j + 1] = 10 * i + j + 1; + fi2d[i][j] = 10 * i + j; + fi2d[i][j + 1] = 10 * i + j + 1; + c2d[i][j / 2] = Complex.ofCartesian(10 * i + j, 10 * i + j + 1); + cr2d[i][j / 2] = Complex.ofReal(10 * i + j / 2); + ci2d[i][j / 2] = Complex.ofCartesian(0, 10 * i + j / 2); + } + } + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + for (int k = 0; k < 20; k += 2) { + d3d[i][j][k / 2] = 100 * i + 10 * j + k / 2; + f3d[i][j][k / 2] = 100 * i + 10 * j + k / 2; + sr3d[i][j][k / 2] = 100 * i + 10 * j + k; + si3d[i][j][k / 2] = 100 * i + 10 * j + k + 1; + sfr3d[i][j][k / 2] = 100 * i + 10 * j + k; + sfi3d[i][j][k / 2] = 100 * i + 10 * j + k + 1; + di3d[i][j][k] = 100 * i + 10 * j + k; + di3d[i][j][k + 1] = 100 * i + 10 * j + k + 1; + fi3d[i][j][k] = 100 * i + 10 * j + k; + fi3d[i][j][k + 1] = 100 * i + 10 * j + k + 1; + c3d[i][j][k / 2] = Complex.ofCartesian(100 * i + 10 * j + k, 100 * i + 10 * j + k + 1); + cr3d[i][j][k / 2] = Complex.ofReal(100 * i + 10 * j + k / 2); + ci3d[i][j][k / 2] = Complex.ofCartesian(0, 100 * i + 10 * j + k / 2); + } + } + } + ansArrayc1r = new Complex[] { Complex.ofReal(3), Complex.ofReal(4), Complex.ofReal(5), Complex.ofReal(6), Complex.ofReal(7) }; + ansArrayc2r = new Complex[] { Complex.ofReal(3), Complex.ofReal(5), Complex.ofReal(7) }; + ansArrayc1i = new Complex[] { Complex.ofCartesian(0, 3), Complex.ofCartesian(0, 4), Complex.ofCartesian(0, 5), Complex.ofCartesian(0, 6), + Complex.ofCartesian(0, 7) }; + ansArrayc2i = new Complex[] { Complex.ofCartesian(0, 3), Complex.ofCartesian(0, 5), Complex.ofCartesian(0, 7) }; + ansArrayc3 = new Complex[] { Complex.ofCartesian(6, 7), Complex.ofCartesian(8, 9), Complex.ofCartesian(10, 11), Complex.ofCartesian(12, 13), + Complex.ofCartesian(14, 15) }; + ansArrayc4 = new Complex[] { Complex.ofCartesian(6, 7), Complex.ofCartesian(10, 11), Complex.ofCartesian(14, 15) }; + ansArrayd1r = new double[] { 6, 8, 10, 12, 14 }; + ansArrayd1i = new double[] { 7, 9, 11, 13, 15 }; + ansArrayd2r = new double[] { 6, 10, 14 }; + ansArrayd2i = new double[] { 7, 11, 15 }; + ansArrayf1r = new float[] { 6, 8, 10, 12, 14 }; + ansArrayf1i = new float[] { 7, 9, 11, 13, 15 }; + ansArrayf2r = new float[] { 6, 10, 14 }; + ansArrayf2i = new float[] { 7, 11, 15 }; + ansArraydi1 = new double[] { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + ansArrayfi1 = new float[] { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + ansArraydi2 = new double[] { 6, 7, 10, 11, 14, 15 }; + ansArrayfi2 = new float[] { 6, 7, 10, 11, 14, 15 }; + msg = ""; + } + + @Test + public void testPolar2Complex() { + TestUtils.assertEquals(Complex.ONE, ComplexUtils.polar2Complex(1, 0), 10e-12); + TestUtils.assertEquals(Complex.ZERO, ComplexUtils.polar2Complex(0, 1), 10e-12); + TestUtils.assertEquals(Complex.ZERO, ComplexUtils.polar2Complex(0, -1), 10e-12); + TestUtils.assertEquals(Complex.I, ComplexUtils.polar2Complex(1, pi / 2), 10e-12); + TestUtils.assertEquals(Complex.I.negate(), ComplexUtils.polar2Complex(1, -pi / 2), 10e-12); + double r = 0; + for (int i = 0; i < 5; i++) { + r += i; + double theta = 0; + for (int j = 0; j < 20; j++) { + theta += pi / 6; + TestUtils.assertEquals(altPolar(r, theta), ComplexUtils.polar2Complex(r, theta), 10e-12); + } + theta = -2 * pi; + for (int j = 0; j < 20; j++) { + theta -= pi / 6; + TestUtils.assertEquals(altPolar(r, theta), ComplexUtils.polar2Complex(r, theta), 10e-12); + } + } + } + + protected Complex altPolar(double r, double theta) { + return Complex.I.multiply(Complex.ofCartesian(theta, 0)).exp().multiply(Complex.ofCartesian(r, 0)); + } + + @Test(expected = IllegalArgumentException.class) + public void testPolar2ComplexIllegalModulus() { + ComplexUtils.polar2Complex(-1, 0); + } + + @Test + public void testPolar2ComplexNaN() { + TestUtils.assertSame(NAN, ComplexUtils.polar2Complex(nan, 1)); + TestUtils.assertSame(NAN, ComplexUtils.polar2Complex(1, nan)); + TestUtils.assertSame(NAN, ComplexUtils.polar2Complex(nan, nan)); + } + + @Test + public void testPolar2ComplexInf() { + TestUtils.assertSame(NAN, ComplexUtils.polar2Complex(1, inf)); + TestUtils.assertSame(NAN, ComplexUtils.polar2Complex(1, negInf)); + TestUtils.assertSame(NAN, ComplexUtils.polar2Complex(inf, inf)); + TestUtils.assertSame(NAN, ComplexUtils.polar2Complex(inf, negInf)); + TestUtils.assertSame(infInf, ComplexUtils.polar2Complex(inf, pi / 4)); + TestUtils.assertSame(infNaN, ComplexUtils.polar2Complex(inf, 0)); + TestUtils.assertSame(infNegInf, ComplexUtils.polar2Complex(inf, -pi / 4)); + TestUtils.assertSame(negInfInf, ComplexUtils.polar2Complex(inf, 3 * pi / 4)); + TestUtils.assertSame(negInfNegInf, ComplexUtils.polar2Complex(inf, 5 * pi / 4)); + } + + @Test + public void testCExtract() { + final double[] real = new double[] { negInf, -123.45, 0, 1, 234.56, pi, inf }; + final Complex[] complex = ComplexUtils.real2Complex(real); + + for (int i = 0; i < real.length; i++) { + Assert.assertEquals(real[i], complex[i].getReal(), 0d); + } + } + + // EXTRACTION METHODS + + @Test + public void testExtractionMethods() { + setArrays(); + // Extract complex from real double array, index 3 + TestUtils.assertSame(Complex.ofReal(3), ComplexUtils.extractComplexFromRealArray(d, 3)); + // Extract complex from real float array, index 3 + TestUtils.assertSame(Complex.ofReal(3), ComplexUtils.extractComplexFromRealArray(f, 3)); + // Extract real double from complex array, index 3 + TestUtils.assertSame(6, ComplexUtils.extractRealFromComplexArray(c, 3)); + // Extract real float from complex array, index 3 + TestUtils.assertSame(6, ComplexUtils.extractRealFloatFromComplexArray(c, 3)); + // Extract complex from interleaved double array, index 3 + TestUtils.assertSame(Complex.ofCartesian(6, 7), ComplexUtils.extractComplexFromInterleavedArray(d, 3)); + // Extract complex from interleaved float array, index 3 + TestUtils.assertSame(Complex.ofCartesian(6, 7), ComplexUtils.extractComplexFromInterleavedArray(f, 3)); + // Extract interleaved double from complex array, index 3 + TestUtils.assertEquals(msg, new double[] { 6, 7 }, ComplexUtils.extractInterleavedFromComplexArray(c, 3), + Math.ulp(1)); + // Extract interleaved float from complex array, index 3 + TestUtils.assertEquals(msg, new double[] { 6, 7 }, ComplexUtils.extractInterleavedFromComplexArray(c, 3), + Math.ulp(1)); + if (!msg.equals("")) { + throw new RuntimeException(msg); + } + } + // REAL <-> COMPLEX + + @Test + public void testRealToComplex() { + setArrays(); + // Real double to complex, range 3-7, increment 1, entered as ints + // Real double to complex, whole array + TestUtils.assertEquals(msg, cr, ComplexUtils.real2Complex(d),Math.ulp(1.0)); + // Real float to complex, whole array + TestUtils.assertEquals(msg, cr, ComplexUtils.real2Complex(f),Math.ulp(1.0)); + // 2d + for (int i = 0; i < 10; i++) { + // Real double to complex, 2d + TestUtils.assertEquals(msg, cr2d[i], ComplexUtils.real2Complex(d2d[i]),Math.ulp(1.0)); + // Real float to complex, 2d + TestUtils.assertEquals(msg, cr2d[i], ComplexUtils.real2Complex(f2d[i]),Math.ulp(1.0)); + } + // 3d + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + // Real double to complex, 3d + TestUtils.assertEquals(msg, cr3d[i][j], ComplexUtils.real2Complex(d3d[i][j]),Math.ulp(1.0)); + // Real float to complex, 3d + TestUtils.assertEquals(msg, cr3d[i][j], ComplexUtils.real2Complex(f3d[i][j]),Math.ulp(1.0)); + } + } + if (!msg.equals("")) { + throw new RuntimeException(msg); + } + } + + @Test + public void testComplexToReal() { + setArrays(); + // Real complex to double, whole array + TestUtils.assertEquals(msg, sr, ComplexUtils.complex2Real(c),Math.ulp(1.0)); + // Real complex to float, whole array + TestUtils.assertEquals(msg, sfr, ComplexUtils.complex2RealFloat(c),Math.ulp(1.0f)); + // 2d + for (int i = 0; i < 10; i++) { + // Real complex to double, 2d + TestUtils.assertEquals(msg, sr2d[i], ComplexUtils.complex2Real(c2d[i]),Math.ulp(1.0)); + // Real complex to float, 2d + TestUtils.assertEquals(msg, sfr2d[i], ComplexUtils.complex2RealFloat(c2d[i]),Math.ulp(1.0f)); + } + // 3d + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + // Real complex to double, 3d + TestUtils.assertEquals(msg, sr3d[i][j], ComplexUtils.complex2Real(c3d[i][j]),Math.ulp(1.0)); + // Real complex to float, 3d + TestUtils.assertEquals(msg, sfr3d[i][j], ComplexUtils.complex2RealFloat(c3d[i][j]),Math.ulp(1.0f)); + } + } + if (!msg.equals("")) { + throw new RuntimeException(msg); + } + } + + // IMAGINARY <-> COMPLEX + + @Test + public void testImaginaryToComplex() { + setArrays(); + // Imaginary double to complex, whole array + TestUtils.assertEquals(msg, ci, ComplexUtils.imaginary2Complex(d),Math.ulp(1.0)); + // Imaginary float to complex, whole array + TestUtils.assertEquals(msg, ci, ComplexUtils.imaginary2Complex(f),Math.ulp(1.0)); + // 2d + for (int i = 0; i < 10; i++) { + // Imaginary double to complex, 2d + TestUtils.assertEquals(msg, ci2d[i], ComplexUtils.imaginary2Complex(d2d[i]),Math.ulp(1.0)); + // Imaginary float to complex, 2d + TestUtils.assertEquals(msg, ci2d[i], ComplexUtils.imaginary2Complex(f2d[i]),Math.ulp(1.0)); + } + // 3d + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + // Imaginary double to complex, 3d + TestUtils.assertEquals(msg, ci3d[i][j], ComplexUtils.imaginary2Complex(d3d[i][j]),Math.ulp(1.0)); + // Imaginary float to complex, 3d + TestUtils.assertEquals(msg, ci3d[i][j], ComplexUtils.imaginary2Complex(f3d[i][j]),Math.ulp(1.0)); + } + } + if (!msg.equals("")) { + throw new RuntimeException(msg); + } + } + + @Test + public void testComplexToImaginary() { + setArrays(); + // Imaginary complex to double, whole array + TestUtils.assertEquals(msg, si, ComplexUtils.complex2Imaginary(c),Math.ulp(1.0)); + // Imaginary complex to float, whole array + TestUtils.assertEquals(msg, sfi, ComplexUtils.complex2ImaginaryFloat(c),Math.ulp(1.0f)); + // 2d + for (int i = 0; i < 10; i++) { + // Imaginary complex to double, 2d + TestUtils.assertEquals(msg, si2d[i], ComplexUtils.complex2Imaginary(c2d[i]),Math.ulp(1.0)); + // Imaginary complex to float, 2d + TestUtils.assertEquals(msg, sfi2d[i], ComplexUtils.complex2ImaginaryFloat(c2d[i]),Math.ulp(1.0f)); + } + // 3d + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + // Imaginary complex to double, 3d + TestUtils.assertEquals(msg, si3d[i][j], ComplexUtils.complex2Imaginary(c3d[i][j]),Math.ulp(1.0)); + // Imaginary complex to float, 3d + TestUtils.assertEquals(msg, sfi3d[i][j], ComplexUtils.complex2ImaginaryFloat(c3d[i][j]),Math.ulp(1.0f)); + } + } + if (!msg.equals("")) { + throw new RuntimeException(msg); + } + } + + // INTERLEAVED <-> COMPLEX + + @Test + public void testInterleavedToComplex() { + setArrays(); + // Interleaved double to complex, whole array + TestUtils.assertEquals(msg, c, ComplexUtils.interleaved2Complex(di),Math.ulp(1.0)); + // Interleaved float to complex, whole array + TestUtils.assertEquals(msg, c, ComplexUtils.interleaved2Complex(fi),Math.ulp(1.0)); + // 2d + for (int i = 0; i < 10; i++) { + // Interleaved double to complex, 2d + TestUtils.assertEquals(msg, c2d[i], ComplexUtils.interleaved2Complex(di2d[i]),Math.ulp(1.0)); + // Interleaved float to complex, 2d + TestUtils.assertEquals(msg, c2d[i], ComplexUtils.interleaved2Complex(fi2d[i]),Math.ulp(1.0)); + } + // 3d + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + // Interleaved double to complex, 3d + TestUtils.assertEquals(msg, c3d[i][j], ComplexUtils.interleaved2Complex(di3d[i][j]),Math.ulp(1.0)); + // Interleaved float to complex, 3d + TestUtils.assertEquals(msg, c3d[i][j], ComplexUtils.interleaved2Complex(fi3d[i][j]),Math.ulp(1.0)); + } + } + if (!msg.equals("")) { + throw new RuntimeException(msg); + } + } + + @Test + public void testComplexToInterleaved() { + setArrays(); + TestUtils.assertEquals(msg, di, ComplexUtils.complex2Interleaved(c),Math.ulp(1.0)); + // Interleaved complex to float, whole array + TestUtils.assertEquals(msg, fi, ComplexUtils.complex2InterleavedFloat(c),Math.ulp(1.0f)); + // 2d + for (int i = 0; i < 10; i++) { + // Interleaved complex to double, 2d + TestUtils.assertEquals(msg, di2d[i], ComplexUtils.complex2Interleaved(c2d[i]),Math.ulp(1.0)); + // Interleaved complex to float, 2d + TestUtils.assertEquals(msg, fi2d[i], ComplexUtils.complex2InterleavedFloat(c2d[i]),Math.ulp(1.0f)); + } + // 3d + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + // Interleaved complex to double, 3d + TestUtils.assertEquals(msg, di3d[i][j], ComplexUtils.complex2Interleaved(c3d[i][j]),Math.ulp(1.0)); + // Interleaved complex to float, 3d + TestUtils.assertEquals(msg, fi3d[i][j], ComplexUtils.complex2InterleavedFloat(c3d[i][j]),Math.ulp(1.0f)); + } + } + if (!msg.equals("")) { + throw new RuntimeException(msg); + } + } + + // SPLIT TO COMPLEX + @Test + public void testSplit2Complex() { + setArrays(); + // Split double to complex, whole array + TestUtils.assertEquals(msg, c, ComplexUtils.split2Complex(sr, si),Math.ulp(1.0)); + + // 2d + for (int i = 0; i < 10; i++) { + // Split double to complex, 2d + TestUtils.assertEquals(msg, c2d[i], ComplexUtils.split2Complex(sr2d[i], si2d[i]),Math.ulp(1.0)); + } + // 3d + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + // Split double to complex, 3d + TestUtils.assertEquals(msg, c3d[i][j], ComplexUtils.split2Complex(sr3d[i][j], si3d[i][j]),Math.ulp(1.0)); + } + } + if (!msg.equals("")) { + throw new RuntimeException(msg); + } + } + + // INITIALIZATION METHODS + + @Test + public void testInitialize() { + Complex[] c = new Complex[10]; + ComplexUtils.initialize(c); + for (Complex cc : c) { + TestUtils.assertEquals(Complex.ofCartesian(0, 0), cc, Math.ulp(0)); + } + } +}
http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/40418955/commons-numbers-complex-streams/src/test/java/org/apache/commons/numbers/complex/streams/TestUtils.java ---------------------------------------------------------------------- diff --git a/commons-numbers-complex-streams/src/test/java/org/apache/commons/numbers/complex/streams/TestUtils.java b/commons-numbers-complex-streams/src/test/java/org/apache/commons/numbers/complex/streams/TestUtils.java new file mode 100644 index 0000000..ec370ff --- /dev/null +++ b/commons-numbers-complex-streams/src/test/java/org/apache/commons/numbers/complex/streams/TestUtils.java @@ -0,0 +1,410 @@ +/* + * 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.streams; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.apache.commons.numbers.complex.Complex; +import org.apache.commons.numbers.core.Precision; + +import org.junit.Assert; + +/** + * Test utilities. + * TODO: Cleanup (remove unused and obsolete methods). + */ +class TestUtils { + /** + * Collection of static methods used in math unit tests. + */ + private TestUtils() { + super(); + } + + /** + * Verifies that expected and actual are within delta, or are both NaN or + * infinities of the same sign. + */ + public static void assertEquals(double expected, double actual, double delta) { + Assert.assertEquals(null, expected, actual, delta); + } + + /** + * Verifies that expected and actual are within delta, or are both NaN or + * infinities of the same sign. + */ + public static void assertEquals(String msg, double expected, double actual, double delta) { + // check for NaN + if(Double.isNaN(expected)){ + Assert.assertTrue("" + actual + " is not NaN.", + Double.isNaN(actual)); + } else { + Assert.assertEquals(msg, expected, actual, delta); + } + } + + /** + * Verifies that the two arguments are exactly the same, either + * both NaN or infinities of same sign, or identical floating point values. + */ + public static void assertSame(double expected, double actual) { + Assert.assertEquals(expected, actual, 0); + } + + /** + * Verifies that real and imaginary parts of the two complex arguments + * are exactly the same. Also ensures that NaN / infinite components match. + */ + public static void assertSame(Complex expected, Complex actual) { + assertSame(expected.getReal(), actual.getReal()); + assertSame(expected.getImaginary(), actual.getImaginary()); + } + + /** + * Verifies that real and imaginary parts of the two complex arguments + * differ by at most delta. Also ensures that NaN / infinite components match. + */ + public static void assertEquals(Complex expected, Complex actual, double delta) { + Assert.assertEquals(expected.getReal(), actual.getReal(), delta); + Assert.assertEquals(expected.getImaginary(), actual.getImaginary(), delta); + } + + /** + * Verifies that two double arrays have equal entries, up to tolerance + */ + public static void assertEquals(double expected[], double observed[], double tolerance) { + assertEquals("Array comparison failure", expected, observed, tolerance); + } + + /** + * Serializes an object to a bytes array and then recovers the object from the bytes array. + * Returns the deserialized object. + * + * @param o object to serialize and recover + * @return the recovered, deserialized object + */ + public static Object serializeAndRecover(Object o) { + try { + // serialize the Object + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream so = new ObjectOutputStream(bos); + so.writeObject(o); + + // deserialize the Object + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + ObjectInputStream si = new ObjectInputStream(bis); + return si.readObject(); + } catch (IOException ioe) { + return null; + } catch (ClassNotFoundException cnfe) { + return null; + } + } + + /** + * Verifies that serialization preserves equals and hashCode. + * Serializes the object, then recovers it and checks equals and hash code. + * + * @param object the object to serialize and recover + */ + public static void checkSerializedEquality(Object object) { + Object object2 = serializeAndRecover(object); + Assert.assertEquals("Equals check", object, object2); + Assert.assertEquals("HashCode check", object.hashCode(), object2.hashCode()); + } + + /** + * Verifies that the relative error in actual vs. expected is less than or + * equal to relativeError. If expected is infinite or NaN, actual must be + * the same (NaN or infinity of the same sign). + * + * @param expected expected value + * @param actual observed value + * @param relativeError maximum allowable relative error + */ + public static void assertRelativelyEquals(double expected, double actual, + double relativeError) { + assertRelativelyEquals(null, expected, actual, relativeError); + } + + /** + * Verifies that the relative error in actual vs. expected is less than or + * equal to relativeError. If expected is infinite or NaN, actual must be + * the same (NaN or infinity of the same sign). + * + * @param msg message to return with failure + * @param expected expected value + * @param actual observed value + * @param relativeError maximum allowable relative error + */ + public static void assertRelativelyEquals(String msg, double expected, + double actual, double relativeError) { + if (Double.isNaN(expected)) { + Assert.assertTrue(msg, Double.isNaN(actual)); + } else if (Double.isNaN(actual)) { + Assert.assertTrue(msg, Double.isNaN(expected)); + } else if (Double.isInfinite(actual) || Double.isInfinite(expected)) { + Assert.assertEquals(expected, actual, relativeError); + } else if (expected == 0.0) { + Assert.assertEquals(msg, actual, expected, relativeError); + } else { + double absError = Math.abs(expected) * relativeError; + Assert.assertEquals(msg, expected, actual, absError); + } + } + + /** + * Fails iff values does not contain a number within epsilon of z. + * + * @param msg message to return with failure + * @param values complex array to search + * @param z value sought + * @param epsilon tolerance + */ + public static void assertContains(String msg, Complex[] values, + Complex z, double epsilon) { + for (Complex value : values) { + if (Precision.equals(value.getReal(), z.getReal(), epsilon) && + Precision.equals(value.getImaginary(), z.getImaginary(), epsilon)) { + return; + } + } + Assert.fail(msg + " Unable to find " + z); + } + + /** + * Fails iff values does not contain a number within epsilon of z. + * + * @param values complex array to search + * @param z value sought + * @param epsilon tolerance + */ + public static void assertContains(Complex[] values, + Complex z, double epsilon) { + assertContains(null, values, z, epsilon); + } + + /** + * Fails iff values does not contain a number within epsilon of x. + * + * @param msg message to return with failure + * @param values double array to search + * @param x value sought + * @param epsilon tolerance + */ + public static void assertContains(String msg, double[] values, + double x, double epsilon) { + for (double value : values) { + if (Precision.equals(value, x, epsilon)) { + return; + } + } + Assert.fail(msg + " Unable to find " + x); + } + + /** + * Fails iff values does not contain a number within epsilon of x. + * + * @param values double array to search + * @param x value sought + * @param epsilon tolerance + */ + public static void assertContains(double[] values, double x, + double epsilon) { + assertContains(null, values, x, epsilon); + } + + /** verifies that two arrays are close (sup norm) */ + public static void assertEquals(String msg, double[] expected, double[] observed, double tolerance) { + StringBuilder out = new StringBuilder(msg); + if (expected.length != observed.length) { + out.append("\n Arrays not same length. \n"); + out.append("expected has length "); + out.append(expected.length); + out.append(" observed length = "); + out.append(observed.length); + Assert.fail(out.toString()); + } + boolean failure = false; + for (int i=0; i < expected.length; i++) { + if (!equalsIncludingNaN(expected[i], observed[i], tolerance)) { + failure = true; + out.append("\n Elements at index "); + out.append(i); + out.append(" differ. "); + out.append(" expected = "); + out.append(expected[i]); + out.append(" observed = "); + out.append(observed[i]); + } + } + if (failure) { + Assert.fail(out.toString()); + } + } + + /** verifies that two arrays are close (sup norm) */ + public static void assertEquals(String msg, float[] expected, float[] observed, float tolerance) { + StringBuilder out = new StringBuilder(msg); + if (expected.length != observed.length) { + out.append("\n Arrays not same length. \n"); + out.append("expected has length "); + out.append(expected.length); + out.append(" observed length = "); + out.append(observed.length); + Assert.fail(out.toString()); + } + boolean failure = false; + for (int i=0; i < expected.length; i++) { + if (!equalsIncludingNaN(expected[i], observed[i], tolerance)) { + failure = true; + out.append("\n Elements at index "); + out.append(i); + out.append(" differ. "); + out.append(" expected = "); + out.append(expected[i]); + out.append(" observed = "); + out.append(observed[i]); + } + } + if (failure) { + Assert.fail(out.toString()); + } + } + + /** verifies that two arrays are close (sup norm) */ + public static void assertEquals(String msg, Complex[] expected, Complex[] observed, double tolerance) { + StringBuilder out = new StringBuilder(msg); + if (expected.length != observed.length) { + out.append("\n Arrays not same length. \n"); + out.append("expected has length "); + out.append(expected.length); + out.append(" observed length = "); + out.append(observed.length); + Assert.fail(out.toString()); + } + boolean failure = false; + for (int i=0; i < expected.length; i++) { + if (!equalsIncludingNaN(expected[i].getReal(), observed[i].getReal(), tolerance)) { + failure = true; + out.append("\n Real elements at index "); + out.append(i); + out.append(" differ. "); + out.append(" expected = "); + out.append(expected[i].getReal()); + out.append(" observed = "); + out.append(observed[i].getReal()); + } + if (!equalsIncludingNaN(expected[i].getImaginary(), observed[i].getImaginary(), tolerance)) { + failure = true; + out.append("\n Imaginary elements at index "); + out.append(i); + out.append(" differ. "); + out.append(" expected = "); + out.append(expected[i].getImaginary()); + out.append(" observed = "); + out.append(observed[i].getImaginary()); + } + } + if (failure) { + Assert.fail(out.toString()); + } + } + + /** + * Updates observed counts of values in quartiles. + * counts[0] <-> 1st quartile ... counts[3] <-> top quartile + */ + public static void updateCounts(double value, long[] counts, double[] quartiles) { + if (value < quartiles[0]) { + counts[0]++; + } else if (value > quartiles[2]) { + counts[3]++; + } else if (value > quartiles[1]) { + counts[2]++; + } else { + counts[1]++; + } + } + + /** + * Eliminates points with zero mass from densityPoints and densityValues parallel + * arrays. Returns the number of positive mass points and collapses the arrays so + * that the first <returned value> elements of the input arrays represent the positive + * mass points. + */ + public static int eliminateZeroMassPoints(int[] densityPoints, double[] densityValues) { + int positiveMassCount = 0; + for (int i = 0; i < densityValues.length; i++) { + if (densityValues[i] > 0) { + positiveMassCount++; + } + } + if (positiveMassCount < densityValues.length) { + int[] newPoints = new int[positiveMassCount]; + double[] newValues = new double[positiveMassCount]; + int j = 0; + for (int i = 0; i < densityValues.length; i++) { + if (densityValues[i] > 0) { + newPoints[j] = densityPoints[i]; + newValues[j] = densityValues[i]; + j++; + } + } + System.arraycopy(newPoints,0,densityPoints,0,positiveMassCount); + System.arraycopy(newValues,0,densityValues,0,positiveMassCount); + } + return positiveMassCount; + } + + /** + * Returns true if the arguments are both NaN, are equal or are within the range + * of allowed error (inclusive). + * + * @param x first value + * @param y second value + * @param eps the amount of absolute error to allow. + * @return {@code true} if the values are equal or within range of each other, + * or both are NaN. + * @since 2.2 + */ + private static boolean equalsIncludingNaN(double x, double y, double eps) { + return equalsIncludingNaN(x, y) || (Math.abs(y - x) <= eps); + } + + /** + * Returns true if the arguments are both NaN or they are + * equal as defined by {@link #equals(double,double) equals(x, y, 1)}. + * + * @param x first value + * @param y second value + * @return {@code true} if the values are equal or both are NaN. + * @since 2.2 + */ + private static boolean equalsIncludingNaN(double x, double y) { + return (x != x || y != y) ? !(x != x ^ y != y) : Precision.equals(x, y, 1); + } + + +} + +
