This is an automated email from the ASF dual-hosted git repository. erans pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-numbers.git
commit a77a8eb22b9527c50e50eac106a6821676a63fba Author: Gilles Sadowski <gillese...@gmail.com> AuthorDate: Sun Jun 13 00:16:58 2021 +0200 NUMBERS-156: Norm API. --- .../org/apache/commons/numbers/angle/CosAngle.java | 4 +- .../org/apache/commons/numbers/core/Norms.java | 448 --------------- .../org/apache/commons/numbers/core/NormsTest.java | 625 --------------------- 3 files changed, 2 insertions(+), 1075 deletions(-) diff --git a/commons-numbers-angle/src/main/java/org/apache/commons/numbers/angle/CosAngle.java b/commons-numbers-angle/src/main/java/org/apache/commons/numbers/angle/CosAngle.java index cfa569d..d9d7dd4 100644 --- a/commons-numbers-angle/src/main/java/org/apache/commons/numbers/angle/CosAngle.java +++ b/commons-numbers-angle/src/main/java/org/apache/commons/numbers/angle/CosAngle.java @@ -17,7 +17,7 @@ package org.apache.commons.numbers.angle; import org.apache.commons.numbers.core.LinearCombination; -import org.apache.commons.numbers.core.Norms; +import org.apache.commons.numbers.core.Norm; /** * Computes the cosine of the angle between two vectors. @@ -39,7 +39,7 @@ public final class CosAngle { */ public static double value(double[] v1, double[] v2) { - return LinearCombination.value(v1, v2) / Norms.euclidean(v1) / Norms.euclidean(v2); + return LinearCombination.value(v1, v2) / Norm.L2.of(v1) / Norm.L2.of(v2); } } diff --git a/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/Norms.java b/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/Norms.java deleted file mode 100644 index 2a357ca..0000000 --- a/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/Norms.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * 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.core; - -/** Class providing methods to compute various norm values. - * - * <p>This class uses a variety of techniques to increase numerical accuracy - * and reduce errors. A primary source for the included algorithms is the - * 2005 paper <a href="https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.2.1547"> - * Accurate Sum and Dot Product</a> by Takeshi Ogita, Siegfried M. Rump, - * and Shin'ichi Oishi published in <em>SIAM J. Sci. Comput</em>. - * @see <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)">Norm</a> - */ -public final class Norms { - - /** Threshold for scaling small numbers. This value is chosen such that doubles - * set to this value can be squared without underflow. Values less than this must - * be scaled up. - */ - private static final double SMALL_THRESH = 0x1.0p-511; - - /** Threshold for scaling large numbers. This value is chosen such that 2^31 doubles - * set to this value can be squared and added without overflow. Values greater than - * this must be scaled down. - */ - private static final double LARGE_THRESH = 0x1.0p+496; - - /** Threshold for scaling up a single value by {@link #SCALE_UP} without risking overflow - * when the value is squared. - */ - private static final double SAFE_SCALE_UP_THRESH = 0x1.0p-100; - - /** Value used to scale down large numbers. */ - private static final double SCALE_DOWN = 0x1.0p-600; - - /** Value used to scale up small numbers. */ - private static final double SCALE_UP = 0x1.0p+600; - - /** Threshold for the difference between the exponents of two Euclidean 2D input values - * where the larger value dominates the calculation. - */ - private static final int EXP_DIFF_THRESHOLD_2D = 54; - - /** Utility class; no instantiation. */ - private Norms() {} - - /** Compute the Manhattan norm (also known as the Taxicab norm or L1 norm) of the arguments. - * The result is equal to \(|x| + |y|\), i.e., the sum of the absolute values of the arguments. - * - * <p>Special cases: - * <ul> - * <li>If either value is NaN, then the result is NaN.</li> - * <li>If either value is infinite and the other value is not NaN, then the result is positive infinity.</li> - * </ul> - * @param x first input value - * @param y second input value - * @return Manhattan norm - * @see <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Taxicab_norm_or_Manhattan_norm">Manhattan norm</a> - */ - public static double manhattan(final double x, final double y) { - return Math.abs(x) + Math.abs(y); - } - - /** Compute the Manhattan norm (also known as the Taxicab norm or L1 norm) of the arguments. - * The result is equal to \(|x| + |y| + |z|\), i.e., the sum of the absolute values of the arguments. - * - * <p>Special cases: - * <ul> - * <li>If any value is NaN, then the result is NaN.</li> - * <li>If any value is infinite and no value is NaN, then the result is positive infinity.</li> - * </ul> - * @param x first input value - * @param y second input value - * @param z third input value - * @return Manhattan norm - * @see <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Taxicab_norm_or_Manhattan_norm">Manhattan norm</a> - */ - public static double manhattan(final double x, final double y, final double z) { - return Summation.value( - Math.abs(x), - Math.abs(y), - Math.abs(z)); - } - - /** Compute the Manhattan norm (also known as the Taxicab norm or L1 norm) of the given values. - * The result is equal to \(|v_0| + ... + |v_i|\), i.e., the sum of the absolute values of the input elements. - * - * <p>Special cases: - * <ul> - * <li>If any value is NaN, then the result is NaN.</li> - * <li>If any value is infinite and no value is NaN, then the result is positive infinity.</li> - * <li>If the array is empty, then the result is 0.</li> - * </ul> - * @param v input values - * @return Manhattan norm - * @see <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Taxicab_norm_or_Manhattan_norm">Manhattan norm</a> - */ - public static double manhattan(final double[] v) { - double sum = 0d; - double comp = 0d; - - for (int i = 0; i < v.length; ++i) { - final double x = Math.abs(v[i]); - final double sx = sum + x; - comp += ExtendedPrecision.twoSumLow(sum, x, sx); - sum = sx; - } - - return Summation.summationResult(sum, comp); - } - - /** Compute the Euclidean norm (also known as the L2 norm) of the arguments. The result is equal to - * \(\sqrt{x^2 + y^2}\). This method correctly handles the possibility of overflow or underflow - * during the computation. - * - * <p>Special cases: - * <ul> - * <li>If either value is NaN, then the result is NaN.</li> - * <li>If either value is infinite and the other value is not NaN, then the result is positive infinity.</li> - * </ul> - * - * <p><strong>Comparison with Math.hypot()</strong> - * <p>While not guaranteed to return the same result, this method does provide similar error bounds to - * the JDK's Math.hypot() method and may run faster on some JVMs. - * @param x first input - * @param y second input - * @return Euclidean norm - * @see <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Euclidean_norm">Euclidean norm</a> - */ - public static double euclidean(final double x, final double y) { - final double xabs = Math.abs(x); - final double yabs = Math.abs(y); - - final double max; - final double min; - // the compare method considers NaN greater than other values, meaning that our - // check for if the max is finite later on will detect NaNs correctly - if (Double.compare(xabs, yabs) > 0) { - max = xabs; - min = yabs; - } else { - max = yabs; - min = xabs; - } - - // if the max is not finite, then one of the inputs must not have - // been finite - if (!Double.isFinite(max)) { - // let the standard multiply operation determine whether to return NaN or infinite - return xabs * yabs; - } else if (Math.getExponent(max) - Math.getExponent(min) > EXP_DIFF_THRESHOLD_2D) { - // value is completely dominated by max; just return max - return max; - } - - // compute the scale and rescale values - final double scale; - final double rescale; - if (max > LARGE_THRESH) { - scale = SCALE_DOWN; - rescale = SCALE_UP; - } else if (max < SAFE_SCALE_UP_THRESH) { - scale = SCALE_UP; - rescale = SCALE_DOWN; - } else { - scale = 1d; - rescale = 1d; - } - - double sum = 0d; - double comp = 0d; - - // add scaled x - double sx = xabs * scale; - final double px = sx * sx; - comp += ExtendedPrecision.squareLowUnscaled(sx, px); - final double sumPx = sum + px; - comp += ExtendedPrecision.twoSumLow(sum, px, sumPx); - sum = sumPx; - - // add scaled y - double sy = yabs * scale; - final double py = sy * sy; - comp += ExtendedPrecision.squareLowUnscaled(sy, py); - final double sumPy = sum + py; - comp += ExtendedPrecision.twoSumLow(sum, py, sumPy); - sum = sumPy; - - return Math.sqrt(sum + comp) * rescale; - } - - /** Compute the Euclidean norm (also known as the L2 norm) of the arguments. The result is equal to - * \(\sqrt{x^2 + y^2 + z^2}\). This method correctly handles the possibility of overflow or underflow - * during the computation. - * - * <p>Special cases: - * <ul> - * <li>If any value is NaN, then the result is NaN.</li> - * <li>If any value is infinite and no value is NaN, then the result is positive infinity.</li> - * </ul> - * @param x first input - * @param y second input - * @param z third input - * @return Euclidean norm - * @see <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Euclidean_norm">Euclidean norm</a> - */ - public static double euclidean(final double x, final double y, final double z) { - final double xabs = Math.abs(x); - final double yabs = Math.abs(y); - final double zabs = Math.abs(z); - - final double max = Math.max(Math.max(xabs, yabs), zabs); - - // if the max is not finite, then one of the inputs must not have - // been finite - if (!Double.isFinite(max)) { - // let the standard multiply operation determine whether to return NaN or infinite - return xabs * yabs * zabs; - } - - // compute the scale and rescale values - final double scale; - final double rescale; - if (max > LARGE_THRESH) { - scale = SCALE_DOWN; - rescale = SCALE_UP; - } else if (max < SAFE_SCALE_UP_THRESH) { - scale = SCALE_UP; - rescale = SCALE_DOWN; - } else { - scale = 1d; - rescale = 1d; - } - - double sum = 0d; - double comp = 0d; - - // add scaled x - double sx = xabs * scale; - final double px = sx * sx; - comp += ExtendedPrecision.squareLowUnscaled(sx, px); - final double sumPx = sum + px; - comp += ExtendedPrecision.twoSumLow(sum, px, sumPx); - sum = sumPx; - - // add scaled y - double sy = yabs * scale; - final double py = sy * sy; - comp += ExtendedPrecision.squareLowUnscaled(sy, py); - final double sumPy = sum + py; - comp += ExtendedPrecision.twoSumLow(sum, py, sumPy); - sum = sumPy; - - // add scaled z - final double sz = zabs * scale; - final double pz = sz * sz; - comp += ExtendedPrecision.squareLowUnscaled(sz, pz); - final double sumPz = sum + pz; - comp += ExtendedPrecision.twoSumLow(sum, pz, sumPz); - sum = sumPz; - - return Math.sqrt(sum + comp) * rescale; - } - - /** Compute the Euclidean norm (also known as the L2 norm) of the given values. The result is equal to - * \(\sqrt{v_0^2 + ... + v_{n-1}^2}\). This method correctly handles the possibility of overflow or underflow - * during the computation. - * - * <p>Special cases: - * <ul> - * <li>If any value is NaN, then the result is NaN.</li> - * <li>If any value is infinite and no value is NaN, then the result is positive infinity.</li> - * <li>If the array is empty, then the result is 0.</li> - * </ul> - * @param v input values - * @return Euclidean norm - * @see <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Euclidean_norm">Euclidean norm</a> - */ - public static double euclidean(final double[] v) { - // sum of big, normal and small numbers - double s1 = 0; - double s2 = 0; - double s3 = 0; - - // sum compensation values - double c1 = 0; - double c2 = 0; - double c3 = 0; - - for (int i = 0; i < v.length; ++i) { - final double x = Math.abs(v[i]); - if (!Double.isFinite(x)) { - // not finite; determine whether to return NaN or positive infinity - return euclideanNormSpecial(v, i); - } else if (x > LARGE_THRESH) { - // scale down - final double sx = x * SCALE_DOWN; - - // compute the product and product compensation - final double p = sx * sx; - final double cp = ExtendedPrecision.squareLowUnscaled(sx, p); - - // compute the running sum and sum compensation - final double s = s1 + p; - final double cs = ExtendedPrecision.twoSumLow(s1, p, s); - - // update running totals - c1 += cp + cs; - s1 = s; - } else if (x < SMALL_THRESH) { - // scale up - final double sx = x * SCALE_UP; - - // compute the product and product compensation - final double p = sx * sx; - final double cp = ExtendedPrecision.squareLowUnscaled(sx, p); - - // compute the running sum and sum compensation - final double s = s3 + p; - final double cs = ExtendedPrecision.twoSumLow(s3, p, s); - - // update running totals - c3 += cp + cs; - s3 = s; - } else { - // no scaling - // compute the product and product compensation - final double p = x * x; - final double cp = ExtendedPrecision.squareLowUnscaled(x, p); - - // compute the running sum and sum compensation - final double s = s2 + p; - final double cs = ExtendedPrecision.twoSumLow(s2, p, s); - - // update running totals - c2 += cp + cs; - s2 = s; - } - } - - // The highest sum is the significant component. Add the next significant. - // Note that the "x * SCALE_DOWN * SCALE_DOWN" expressions must be executed - // in the order given. If the two scale factors are multiplied together first, - // they will underflow to zero. - if (s1 != 0) { - // add s1, s2, c1, c2 - final double s2Adj = s2 * SCALE_DOWN * SCALE_DOWN; - final double sum = s1 + s2Adj; - final double comp = ExtendedPrecision.twoSumLow(s1, s2Adj, sum) + c1 + (c2 * SCALE_DOWN * SCALE_DOWN); - return Math.sqrt(sum + comp) * SCALE_UP; - } else if (s2 != 0) { - // add s2, s3, c2, c3 - final double s3Adj = s3 * SCALE_DOWN * SCALE_DOWN; - final double sum = s2 + s3Adj; - final double comp = ExtendedPrecision.twoSumLow(s2, s3Adj, sum) + c2 + (c3 * SCALE_DOWN * SCALE_DOWN); - return Math.sqrt(sum + comp); - } - // add s3, c3 - return Math.sqrt(s3 + c3) * SCALE_DOWN; - } - - /** Return a Euclidean norm value for special cases of non-finite input. - * @param v input vector - * @param start index to start examining the input vector from - * @return Euclidean norm special value - */ - private static double euclideanNormSpecial(final double[] v, final int start) { - for (int i = start; i < v.length; ++i) { - if (Double.isNaN(v[i])) { - return Double.NaN; - } - } - return Double.POSITIVE_INFINITY; - } - - /** Compute the maximum norm (also known as the infinity norm or L<sub>inf</sub> norm) of the arguments. - * The result is equal to \(\max{(|x|, |y|)}\), i.e., the maximum of the absolute values of the arguments. - * - * <p>Special cases: - * <ul> - * <li>If either value is NaN, then the result is NaN.</li> - * <li>If either value is infinite and the other value is not NaN, then the result is positive infinity.</li> - * </ul> - * @param x first input - * @param y second input - * @return maximum norm - * @see <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Maximum_norm_(special_case_of:_infinity_norm,_uniform_norm,_or_supremum_norm)">Maximum norm</a> - */ - public static double maximum(final double x, final double y) { - return Math.max(Math.abs(x), Math.abs(y)); - } - - /** Compute the maximum norm (also known as the infinity norm or L<sub>inf</sub> norm) of the arguments. - * The result is equal to \(\max{(|x|, |y|, |z|)}\), i.e., the maximum of the absolute values of the arguments. - * - * <p>Special cases: - * <ul> - * <li>If any value is NaN, then the result is NaN.</li> - * <li>If any value is infinite and no value is NaN, then the result is positive infinity.</li> - * </ul> - * @param x first input - * @param y second input - * @param z third input - * @return maximum norm - * @see <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Maximum_norm_(special_case_of:_infinity_norm,_uniform_norm,_or_supremum_norm)">Maximum norm</a> - */ - public static double maximum(final double x, final double y, final double z) { - return Math.max( - Math.abs(x), - Math.max(Math.abs(y), Math.abs(z))); - } - - /** Compute the maximum norm (also known as the infinity norm or L<sub>inf</sub> norm) of the given values. - * The result is equal to \(\max{(|v_0|, \ldots, |v_{n-1}|)}\), i.e., the maximum of the absolute values of the - * input elements. - * - * <p>Special cases: - * <ul> - * <li>If any value is NaN, then the result is NaN.</li> - * <li>If any value is infinite and no value is NaN, then the result is positive infinity.</li> - * <li>If the array is empty, then the result is 0.</li> - * </ul> - * @param v input values - * @return maximum norm - * @see <a href="https://en.wikipedia.org/wiki/Norm_(mathematics)#Maximum_norm_(special_case_of:_infinity_norm,_uniform_norm,_or_supremum_norm)">Maximum norm</a> - */ - public static double maximum(final double[] v) { - double max = 0d; - for (int i = 0; i < v.length; ++i) { - max = Math.max(max, Math.abs(v[i])); - } - return max; - } -} diff --git a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/NormsTest.java b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/NormsTest.java deleted file mode 100644 index aa1496d..0000000 --- a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/NormsTest.java +++ /dev/null @@ -1,625 +0,0 @@ -/* - * 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.core; - -import java.math.BigDecimal; -import java.math.MathContext; -import java.util.Arrays; -import java.util.function.ToDoubleFunction; - -import org.apache.commons.rng.UniformRandomProvider; -import org.apache.commons.rng.simple.RandomSource; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class NormsTest { - - private static final int SMALL_THRESH_EXP = -511; - - private static final int LARGE_THRESH_EXP = +496; - - private static final int RAND_VECTOR_CNT = 1_000; - - private static final int MAX_ULP_ERR = 1; - - private static final double HYPOT_COMPARE_EPS = 1e-2; - - private static final BigDecimal BD_MAX_VALUE = new BigDecimal(Double.MAX_VALUE); - private static final BigDecimal BD_MIN_NORMAL = new BigDecimal(Double.MIN_NORMAL); - - /** The scale, used to scale the sqrt of the sum of squares. */ - private static final double SCALE = 0x1.0p200; - - /** The scale squared, used to scale the sum of squares. */ - private static final BigDecimal SCALE2 = new BigDecimal(SCALE * SCALE); - - @Test - void testManhattan_2d() { - // act/assert - Assertions.assertEquals(0d, Norms.manhattan(0d, -0d)); - Assertions.assertEquals(3d, Norms.manhattan(-1d, 2d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, Norms.manhattan(Double.MAX_VALUE, Double.MAX_VALUE)); - - Assertions.assertEquals(Double.NaN, Norms.manhattan(Double.NaN, 1d)); - Assertions.assertEquals(Double.NaN, Norms.manhattan(1d, Double.NaN)); - Assertions.assertEquals(Double.NaN, Norms.manhattan(Double.POSITIVE_INFINITY, Double.NaN)); - - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.manhattan(Double.POSITIVE_INFINITY, 0d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.manhattan(0d, Double.POSITIVE_INFINITY)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.manhattan(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); - } - - @Test - void testManhattan_3d() { - // act/assert - Assertions.assertEquals(0d, Norms.manhattan(0d, -0d, 0d)); - Assertions.assertEquals(6d, Norms.manhattan(-1d, 2d, -3d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, Norms.manhattan(Double.MAX_VALUE, Double.MAX_VALUE, 0d)); - - Assertions.assertEquals(Double.NaN, Norms.manhattan(Double.NaN, -2d, 1d)); - Assertions.assertEquals(Double.NaN, Norms.manhattan(-2d, Double.NaN, 1d)); - Assertions.assertEquals(Double.NaN, Norms.manhattan(-2d, 1d, Double.NaN)); - Assertions.assertEquals(Double.NaN, Norms.manhattan(-2d, Double.POSITIVE_INFINITY, Double.NaN)); - - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.manhattan(Double.POSITIVE_INFINITY, 2d, -4d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.manhattan(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, -4d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.manhattan(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); - } - - @Test - void testManhattan_array() { - // act/assert - Assertions.assertEquals(0d, Norms.manhattan(new double[0])); - Assertions.assertEquals(0d, Norms.manhattan(new double[] {0d, -0d})); - Assertions.assertEquals(6d, Norms.manhattan(new double[] {-1d, 2d, -3d})); - Assertions.assertEquals(10d, Norms.manhattan(new double[] {-1d, 2d, -3d, 4d})); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.manhattan(new double[] {Double.MAX_VALUE, Double.MAX_VALUE})); - - Assertions.assertEquals(Double.NaN, Norms.manhattan(new double[] {-2d, Double.NaN, 1d})); - Assertions.assertEquals(Double.NaN, Norms.manhattan(new double[] {Double.POSITIVE_INFINITY, Double.NaN, 1d})); - - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.manhattan(new double[] {Double.POSITIVE_INFINITY, 0d})); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.manhattan(new double[] {Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY})); - } - - @Test - void testEuclidean_2d_simple() { - // act/assert - Assertions.assertEquals(0d, Norms.euclidean(0d, 0d)); - Assertions.assertEquals(1d, Norms.euclidean(1d, 0d)); - Assertions.assertEquals(1d, Norms.euclidean(0d, 1d)); - Assertions.assertEquals(5d, Norms.euclidean(-3d, 4d)); - Assertions.assertEquals(Double.MIN_VALUE, Norms.euclidean(0d, Double.MIN_VALUE)); - Assertions.assertEquals(Double.MAX_VALUE, Norms.euclidean(Double.MAX_VALUE, 0d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, Norms.euclidean(Double.MAX_VALUE, Double.MAX_VALUE)); - - Assertions.assertEquals(Math.sqrt(2), Norms.euclidean(1d, -1d)); - - Assertions.assertEquals(Double.NaN, Norms.euclidean(Double.NaN, -2d)); - Assertions.assertEquals(Double.NaN, Norms.euclidean(Double.NaN, Double.POSITIVE_INFINITY)); - Assertions.assertEquals(Double.NaN, Norms.euclidean(-2d, Double.NaN)); - Assertions.assertEquals(Double.NaN, - Norms.euclidean(Double.NaN, Double.NEGATIVE_INFINITY)); - - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.euclidean(1d, Double.NEGATIVE_INFINITY)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.euclidean(Double.POSITIVE_INFINITY, -1d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.euclidean(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); - } - - @Test - void testEuclidean_2d_scaled() { - // arrange - final double[] ones = new double[] {1, 1}; - final double[] multiplesOfTen = new double[] {1, 10}; - final ToDoubleFunction<double[]> fn = v -> Norms.euclidean(v[0], v[1]); - - // act/assert - checkScaledEuclideanNorm(ones, 0, fn); - checkScaledEuclideanNorm(ones, LARGE_THRESH_EXP, fn); - checkScaledEuclideanNorm(ones, LARGE_THRESH_EXP + 1, fn); - checkScaledEuclideanNorm(ones, -100, fn); - checkScaledEuclideanNorm(ones, -101, fn); - checkScaledEuclideanNorm(ones, SMALL_THRESH_EXP, fn); - checkScaledEuclideanNorm(ones, SMALL_THRESH_EXP - 1, fn); - - - checkScaledEuclideanNorm(multiplesOfTen, 0, fn); - checkScaledEuclideanNorm(multiplesOfTen, -100, fn); - checkScaledEuclideanNorm(multiplesOfTen, -101, fn); - checkScaledEuclideanNorm(multiplesOfTen, LARGE_THRESH_EXP - 1, fn); - checkScaledEuclideanNorm(multiplesOfTen, SMALL_THRESH_EXP, fn); - } - - @Test - void testEuclidean_2d_dominantValue() { - // act/assert - Assertions.assertEquals(Math.PI, Norms.euclidean(-Math.PI, 0x1.0p-55)); - Assertions.assertEquals(Math.PI, Norms.euclidean(0x1.0p-55, -Math.PI)); - } - - @Test - void testEuclidean_2d_random() { - // arrange - final UniformRandomProvider rng = RandomSource.create(RandomSource.XO_RO_SHI_RO_1024_PP, 1L); - - // act/assert - checkEuclideanRandom(2, rng, v -> Norms.euclidean(v[0], v[1])); - } - - @Test - void testEuclidean_2d_vsArray() { - // arrange - final double[][] inputs = { - {-4.074598908124454E-9, 9.897869969944898E-28}, - {1.3472131556526359E-27, -9.064577177323565E9}, - {-3.9219339341360245E149, -7.132522817112096E148}, - {-1.4888098520466735E153, -2.9099184907796666E150}, - {-8.659395144898396E-152, -1.123275532302136E-150}, - {-3.660198254902351E-152, -6.656524053354807E-153} - }; - - // act/assert - for (final double[] input : inputs) { - Assertions.assertEquals(Norms.euclidean(input), Norms.euclidean(input[0], input[1]), - () -> "Expected inline method result to equal array result for input " + Arrays.toString(input)); - } - } - - @Test - void testEuclidean_2d_vsHypot() { - // arrange - final int samples = 1000; - final UniformRandomProvider rng = RandomSource.create(RandomSource.XO_RO_SHI_RO_1024_PP, 3L); - - // act/assert - assertEuclidean2dVersusHypot(-10, +10, samples, rng); - assertEuclidean2dVersusHypot(0, +20, samples, rng); - assertEuclidean2dVersusHypot(-20, 0, samples, rng); - assertEuclidean2dVersusHypot(-20, +20, samples, rng); - assertEuclidean2dVersusHypot(-100, +100, samples, rng); - assertEuclidean2dVersusHypot(LARGE_THRESH_EXP - 10, LARGE_THRESH_EXP + 10, samples, rng); - assertEuclidean2dVersusHypot(SMALL_THRESH_EXP - 10, SMALL_THRESH_EXP + 10, samples, rng); - assertEuclidean2dVersusHypot(-600, +600, samples, rng); - } - - /** Assert that the Norms euclidean 2D computation produces similar error behavior to Math.hypot(). - * @param minExp minimum exponent for random inputs - * @param maxExp maximum exponent for random inputs - * @param samples sample count - * @param rng random number generator - */ - private static void assertEuclidean2dVersusHypot(final int minExp, final int maxExp, final int samples, - final UniformRandomProvider rng) { - // generate random inputs - final double[][] inputs = new double[samples][]; - for (int i = 0; i < samples; ++i) { - inputs[i] = DoubleTestUtils.randomArray(2, minExp, maxExp, rng); - } - - // compute exact results - final double[] exactResults = new double[samples]; - for (int i = 0; i < samples; ++i) { - exactResults[i] = exactEuclideanNorm(inputs[i]); - } - - // compute the std devs - final UlpErrorStats hypotStats = computeUlpErrorStats(inputs, exactResults, v -> Math.hypot(v[0], v[1])); - final UlpErrorStats normStats = computeUlpErrorStats(inputs, exactResults, v -> Norms.euclidean(v[0], v[1])); - - // ensure that we are within the ballpark of Math.hypot - Assertions.assertTrue(normStats.getMean() <= (hypotStats.getMean() + HYPOT_COMPARE_EPS), - () -> "Expected 2D norm result to have similar error mean to Math.hypot(): hypot error mean= " + - hypotStats.getMean() + ", norm error mean= " + normStats.getMean()); - - Assertions.assertTrue(normStats.getStdDev() <= (hypotStats.getStdDev() + HYPOT_COMPARE_EPS), - () -> "Expected 2D norm result to have similar std deviation to Math.hypot(): hypot std dev= " + - hypotStats.getStdDev() + ", norm std dev= " + normStats.getStdDev()); - } - - @Test - void testEuclidean_3d_simple() { - // act/assert - Assertions.assertEquals(0d, Norms.euclidean(0d, 0d, 0d)); - Assertions.assertEquals(1d, Norms.euclidean(1d, 0d, 0d)); - Assertions.assertEquals(1d, Norms.euclidean(0d, 1d, 0d)); - Assertions.assertEquals(1d, Norms.euclidean(0d, 0d, 1d)); - Assertions.assertEquals(5 * Math.sqrt(2), Norms.euclidean(-3d, -4d, 5d)); - Assertions.assertEquals(Double.MIN_VALUE, Norms.euclidean(0d, 0d, Double.MIN_VALUE)); - Assertions.assertEquals(Double.MAX_VALUE, Norms.euclidean(Double.MAX_VALUE, 0d, 0d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.euclidean(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE)); - - Assertions.assertEquals(Math.sqrt(3), Norms.euclidean(1d, -1d, 1d)); - - Assertions.assertEquals(Double.NaN, Norms.euclidean(Double.NaN, -2d, 0d)); - Assertions.assertEquals(Double.NaN, Norms.euclidean(-2d, Double.NaN, 0d)); - Assertions.assertEquals(Double.NaN, Norms.euclidean(-2d, 0d, Double.NaN)); - Assertions.assertEquals(Double.NaN, - Norms.euclidean(Double.POSITIVE_INFINITY, Double.NaN, 1d)); - - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.euclidean(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 1d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.euclidean(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); - } - - @Test - void testEuclidean_3d_scaled() { - // arrange - final double[] ones = new double[] {1, 1, 1}; - final double[] multiplesOfTen = new double[] {1, 10, 100}; - final ToDoubleFunction<double[]> fn = v -> Norms.euclidean(v[0], v[1], v[2]); - - // act/assert - checkScaledEuclideanNorm(ones, 0, fn); - checkScaledEuclideanNorm(ones, LARGE_THRESH_EXP, fn); - checkScaledEuclideanNorm(ones, LARGE_THRESH_EXP + 1, fn); - checkScaledEuclideanNorm(ones, -100, fn); - checkScaledEuclideanNorm(ones, -101, fn); - checkScaledEuclideanNorm(ones, SMALL_THRESH_EXP, fn); - checkScaledEuclideanNorm(ones, SMALL_THRESH_EXP - 1, fn); - - checkScaledEuclideanNorm(multiplesOfTen, 0, fn); - checkScaledEuclideanNorm(multiplesOfTen, -100, fn); - checkScaledEuclideanNorm(multiplesOfTen, -101, fn); - checkScaledEuclideanNorm(multiplesOfTen, LARGE_THRESH_EXP - 1, fn); - checkScaledEuclideanNorm(multiplesOfTen, SMALL_THRESH_EXP - 1, fn); - } - - @Test - void testEuclidean_3d_random() { - // arrange - final UniformRandomProvider rng = RandomSource.create(RandomSource.XO_RO_SHI_RO_1024_PP, 1L); - - // act/assert - checkEuclideanRandom(3, rng, v -> Norms.euclidean(v[0], v[1], v[2])); - } - - @Test - void testEuclidean_3d_vsArray() { - // arrange - final double[][] inputs = { - {-4.074598908124454E-9, 9.897869969944898E-28, 7.849935157082846E-14}, - {1.3472131556526359E-27, -9.064577177323565E9, 323771.526282239}, - {-3.9219339341360245E149, -7.132522817112096E148, -3.427334456813165E147}, - {-1.4888098520466735E153, -2.9099184907796666E150, 1.0144962310234785E152}, - {-8.659395144898396E-152, -1.123275532302136E-150, -2.151505326692001E-152}, - {-3.660198254902351E-152, -6.656524053354807E-153, -3.198606556986218E-154} - }; - - // act/assert - for (final double[] input : inputs) { - Assertions.assertEquals(Norms.euclidean(input), Norms.euclidean(input[0], input[1], input[2]), - () -> "Expected inline method result to equal array result for input " + Arrays.toString(input)); - } - } - - @Test - void testEuclidean_array_simple() { - // act/assert - Assertions.assertEquals(0d, Norms.euclidean(new double[0])); - Assertions.assertEquals(5d, Norms.euclidean(new double[] {-3d, 4d})); - - Assertions.assertEquals(Math.sqrt(2), Norms.euclidean(new double[] {1d, -1d})); - Assertions.assertEquals(Math.sqrt(3), Norms.euclidean(new double[] {1d, -1d, 1d})); - Assertions.assertEquals(2, Norms.euclidean(new double[] {1d, -1d, 1d, -1d})); - - final double[] longVec = new double[] {-0.9, 8.7, -6.5, -4.3, -2.1, 0, 1.2, 3.4, -5.6, 7.8, 9.0}; - Assertions.assertEquals(directEuclideanNorm(longVec), Norms.euclidean(longVec)); - - Assertions.assertEquals(Double.MIN_VALUE, Norms.euclidean(new double[] {0d, Double.MIN_VALUE})); - Assertions.assertEquals(Double.MAX_VALUE, Norms.euclidean(new double[] {Double.MAX_VALUE, 0d})); - - final double[] maxVec = new double[1000]; - Arrays.fill(maxVec, Double.MAX_VALUE); - Assertions.assertEquals(Double.POSITIVE_INFINITY, Norms.euclidean(maxVec)); - - final double[] largeThreshVec = new double[1000]; - Arrays.fill(largeThreshVec, 0x1.0p496); - Assertions.assertEquals(Math.sqrt(largeThreshVec.length) * largeThreshVec[0], Norms.euclidean(largeThreshVec)); - - Assertions.assertEquals(Double.NaN, Norms.euclidean(new double[] {-2d, Double.NaN, 1d})); - Assertions.assertEquals(Double.NaN, - Norms.euclidean(new double[] {Double.POSITIVE_INFINITY, Double.NaN})); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.euclidean(new double[] {Double.POSITIVE_INFINITY, 1, 0})); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.euclidean(new double[] {Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY})); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.euclidean(new double[] {Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY})); - } - - @Test - void testEuclidean_array_scaled() { - // arrange - final double[] ones = new double[] {1, 1, 1, 1}; - final double[] multiplesOfTen = new double[] {1, 10, 100, 1000}; - - // act/assert - checkScaledEuclideanNorm(ones, 0, Norms::euclidean); - checkScaledEuclideanNorm(ones, LARGE_THRESH_EXP, Norms::euclidean); - checkScaledEuclideanNorm(ones, LARGE_THRESH_EXP + 1, Norms::euclidean); - checkScaledEuclideanNorm(ones, SMALL_THRESH_EXP, Norms::euclidean); - checkScaledEuclideanNorm(ones, SMALL_THRESH_EXP - 1, Norms::euclidean); - - checkScaledEuclideanNorm(multiplesOfTen, 1, Norms::euclidean); - checkScaledEuclideanNorm(multiplesOfTen, LARGE_THRESH_EXP - 1, Norms::euclidean); - checkScaledEuclideanNorm(multiplesOfTen, SMALL_THRESH_EXP - 1, Norms::euclidean); - } - - @Test - void testEuclidean_array_random() { - // arrange - final UniformRandomProvider rng = RandomSource.create(RandomSource.XO_RO_SHI_RO_1024_PP, 1L); - - // act/assert - checkEuclideanRandom(2, rng, Norms::euclidean); - checkEuclideanRandom(3, rng, Norms::euclidean); - checkEuclideanRandom(4, rng, Norms::euclidean); - checkEuclideanRandom(10, rng, Norms::euclidean); - checkEuclideanRandom(100, rng, Norms::euclidean); - } - - @Test - void testMaximum_2d() { - // act/assert - Assertions.assertEquals(0d, Norms.maximum(0d, -0d)); - Assertions.assertEquals(2d, Norms.maximum(1d, -2d)); - Assertions.assertEquals(3d, Norms.maximum(3d, 1d)); - Assertions.assertEquals(Double.MAX_VALUE, Norms.maximum(Double.MAX_VALUE, Double.MAX_VALUE)); - - Assertions.assertEquals(Double.NaN, Norms.maximum(Double.NaN, 0d)); - Assertions.assertEquals(Double.NaN, Norms.maximum(0d, Double.NaN)); - Assertions.assertEquals(Double.NaN, Norms.maximum(Double.POSITIVE_INFINITY, Double.NaN)); - - Assertions.assertEquals(Double.POSITIVE_INFINITY, Norms.maximum(Double.POSITIVE_INFINITY, 0d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, Norms.maximum(Double.NEGATIVE_INFINITY, 0d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, Norms.maximum(0d, Double.NEGATIVE_INFINITY)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.maximum(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); - } - - @Test - void testMaximum_3d() { - // act/assert - Assertions.assertEquals(0d, Norms.maximum(0d, -0d, 0d)); - Assertions.assertEquals(3d, Norms.maximum(1d, -2d, 3d)); - Assertions.assertEquals(4d, Norms.maximum(-4d, -2d, 3d)); - Assertions.assertEquals(Double.MAX_VALUE, Norms.maximum(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE)); - - Assertions.assertEquals(Double.NaN, Norms.maximum(Double.NaN, 3d, 0d)); - Assertions.assertEquals(Double.NaN, Norms.maximum(3d, Double.NaN, 0d)); - Assertions.assertEquals(Double.NaN, Norms.maximum(3d, 0d, Double.NaN)); - Assertions.assertEquals(Double.NaN, Norms.maximum(Double.POSITIVE_INFINITY, 0d, Double.NaN)); - - Assertions.assertEquals(Double.POSITIVE_INFINITY, Norms.maximum(Double.POSITIVE_INFINITY, 0d, 1d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, Norms.maximum(0d, Double.POSITIVE_INFINITY, 1d)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, Norms.maximum(0d, 1d, Double.NEGATIVE_INFINITY)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.maximum(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); - } - - @Test - void testMaximum_array() { - // act/assert - Assertions.assertEquals(0d, Norms.maximum(new double[0])); - Assertions.assertEquals(0d, Norms.maximum(new double[] {0d, -0d})); - Assertions.assertEquals(3d, Norms.maximum(new double[] {-1d, 2d, -3d})); - Assertions.assertEquals(4d, Norms.maximum(new double[] {-1d, 2d, -3d, 4d})); - Assertions.assertEquals(Double.MAX_VALUE, Norms.maximum(new double[] {Double.MAX_VALUE, Double.MAX_VALUE})); - - Assertions.assertEquals(Double.NaN, Norms.maximum(new double[] {-2d, Double.NaN, 1d})); - Assertions.assertEquals(Double.NaN, Norms.maximum(new double[] {Double.POSITIVE_INFINITY, Double.NaN})); - - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.maximum(new double[] {0d, Double.POSITIVE_INFINITY})); - Assertions.assertEquals(Double.POSITIVE_INFINITY, - Norms.maximum(new double[] {Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY})); - } - - /** Check a number of random vectors of length {@code len} with various exponent - * ranges. - * @param len vector array length - * @param rng random number generator - * @param fn euclidean norm test function - */ - private static void checkEuclideanRandom(final int len, final UniformRandomProvider rng, - final ToDoubleFunction<double[]> fn) { - checkEuclideanRandom(len, +600, +620, rng, fn); - checkEuclideanRandom(len, LARGE_THRESH_EXP - 10, LARGE_THRESH_EXP + 10, rng, fn); - checkEuclideanRandom(len, +400, +420, rng, fn); - checkEuclideanRandom(len, +100, +120, rng, fn); - checkEuclideanRandom(len, -10, +10, rng, fn); - checkEuclideanRandom(len, -120, -100, rng, fn); - checkEuclideanRandom(len, -420, -400, rng, fn); - checkEuclideanRandom(len, SMALL_THRESH_EXP - 10, SMALL_THRESH_EXP + 10, rng, fn); - checkEuclideanRandom(len, -620, -600, rng, fn); - - checkEuclideanRandom(len, -600, +600, rng, fn); - } - - /** Check a number of random vectors of length {@code len} with elements containing - * exponents in the range {@code [minExp, maxExp]}. - * @param len vector array length - * @param minExp min exponent - * @param maxExp max exponent - * @param rng random number generator - * @param fn euclidean norm test function - */ - private static void checkEuclideanRandom(final int len, final int minExp, final int maxExp, - final UniformRandomProvider rng, final ToDoubleFunction<double[]> fn) { - for (int i = 0; i < RAND_VECTOR_CNT; ++i) { - // arrange - final double[] v = DoubleTestUtils.randomArray(len, minExp, maxExp, rng); - - final double exact = exactEuclideanNorm(v); - final double direct = directEuclideanNorm(v); - - // act - final double actual = fn.applyAsDouble(v); - - // assert - Assertions.assertTrue(Double.isFinite(actual), () -> - "Computed norm was not finite; vector= " + Arrays.toString(v) + ", exact= " + exact + - ", direct= " + direct + ", actual= " + actual); - - final int ulpError = Math.abs(DoubleTestUtils.computeUlpDifference(exact, actual)); - - Assertions.assertTrue(ulpError <= MAX_ULP_ERR, () -> - "Computed norm ulp error exceeds bounds; vector= " + Arrays.toString(v) + - ", exact= " + exact + ", actual= " + actual + ", ulpError= " + ulpError); - } - } - - /** Assert that {@code directNorm(v) * 2^scaleExp = fn(v * 2^scaleExp)}. - * @param v unscaled vector - * @param scaleExp scale factor exponent - * @param fn euclidean norm function - */ - private static void checkScaledEuclideanNorm(final double[] v, final int scaleExp, - final ToDoubleFunction<double[]> fn) { - - final double scale = Math.scalb(1d, scaleExp); - final double[] scaledV = new double[v.length]; - for (int i = 0; i < v.length; ++i) { - scaledV[i] = v[i] * scale; - } - - final double norm = directEuclideanNorm(v); - final double scaledNorm = fn.applyAsDouble(scaledV); - - Assertions.assertEquals(norm * scale, scaledNorm); - } - - /** Direct euclidean norm computation. - * @param v array - * @return euclidean norm using direct summation. - */ - private static double directEuclideanNorm(final double[] v) { - double n = 0; - for (int i = 0; i < v.length; i++) { - n += v[i] * v[i]; - } - return Math.sqrt(n); - } - - /** Compute the exact double value of the vector norm using BigDecimals - * with a math context of {@link MathContext#DECIMAL128}. - * @param v array - * @return euclidean norm using BigDecimal with MathContext.DECIMAL128 - */ - private static double exactEuclideanNorm(final double[] v) { - final MathContext ctx = MathContext.DECIMAL128; - - BigDecimal sum = BigDecimal.ZERO; - for (final double d : v) { - sum = sum.add(new BigDecimal(d).pow(2), ctx); - } - - // Java 9+: - // sum.sqrt(ctx).doubleValue() - - // Require the sum to be in the range of a double for conversion before sqrt(). - // We scale by a power of 2. Rescaling uses the square root of this which is also - // a power of 2 and can be accumulated for exact rescaling. - double rescale = 1.0; - if (sum.compareTo(BD_MIN_NORMAL) < 0) { - while (sum.compareTo(BD_MIN_NORMAL) < 0) { - sum = sum.multiply(SCALE2); - rescale /= SCALE; - } - } else if (sum.compareTo(BD_MAX_VALUE) > 0) { - while (sum.compareTo(BD_MAX_VALUE) > 0) { - sum = sum.divide(SCALE2); - rescale *= SCALE; - } - } - - return Math.sqrt(sum.doubleValue()) * rescale; - } - - /** Compute statistics for the ulp error of {@code fn} for the given inputs and - * array of exact results. - * @param inputs sample inputs - * @param exactResults array containing the exact expected results - * @param fn function to perform the computation - * @return ulp error statistics - */ - private static UlpErrorStats computeUlpErrorStats(final double[][] inputs, final double[] exactResults, - final ToDoubleFunction<double[]> fn) { - - // compute the ulp errors for each input - final int[] ulpErrors = new int[inputs.length]; - int sum = 0; - for (int i = 0; i < inputs.length; ++i) { - final double exact = exactResults[i]; - final double actual = fn.applyAsDouble(inputs[i]); - - final int error = DoubleTestUtils.computeUlpDifference(exact, actual); - ulpErrors[i] = error; - sum += error; - } - - // compute the mean - final double mean = sum / (double) ulpErrors.length; - - // compute the std dev - double diffSumSq = 0d; - double diff; - for (int ulpError : ulpErrors) { - diff = ulpError - mean; - diffSumSq += diff * diff; - } - - final double stdDev = Math.sqrt(diffSumSq / (inputs.length - 1)); - - return new UlpErrorStats(mean, stdDev); - } - - /** Class containing ULP error statistics. */ - private static final class UlpErrorStats { - - private final double mean; - - private final double stdDev; - - UlpErrorStats(final double mean, final double stdDev) { - this.mean = mean; - this.stdDev = stdDev; - } - - public double getMean() { - return mean; - } - - public double getStdDev() { - return stdDev; - } - } -}