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

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


The following commit(s) were added to refs/heads/master by this push:
     new d85d307c STATISTICS-97: Check for arguments that invalidate the test 
statistic
d85d307c is described below

commit d85d307c8568e32c9727ecc250dc60ca60f36fb9
Author: Alex Herbert <[email protected]>
AuthorDate: Thu Aug 27 13:16:53 2026 +0100

    STATISTICS-97: Check for arguments that invalidate the test statistic
---
 .../commons/statistics/inference/Arguments.java    | 32 ++++++++++++++++------
 .../statistics/inference/InferenceException.java   |  4 +++
 .../commons/statistics/inference/OneWayAnova.java  | 16 +++++++----
 .../apache/commons/statistics/inference/TTest.java | 28 +++++++++++++------
 .../statistics/inference/ArgumentsTest.java        | 22 ++++++++++-----
 .../statistics/inference/OneWayAnovaTest.java      |  6 ++++
 .../commons/statistics/inference/TTestTest.java    | 32 ++++++++++++++++++++++
 src/changes/changes.xml                            |  4 +++
 8 files changed, 114 insertions(+), 30 deletions(-)

diff --git 
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/Arguments.java
 
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/Arguments.java
index e7a38441..b15ddb4f 100644
--- 
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/Arguments.java
+++ 
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/Arguments.java
@@ -63,7 +63,7 @@ final class Arguments {
      * Check that the value is {@code >= 0}.
      *
      * @param v Value to be tested.
-     * @throws IllegalArgumentException if the value is less than 0.
+     * @throws IllegalArgumentException if the value is less than 0, or is NaN.
      */
     static void checkNonNegative(double v) {
         if (v >= 0) {
@@ -118,7 +118,7 @@ final class Arguments {
      *
      * @param v Value to be tested.
      * @return the value
-     * @throws IllegalArgumentException if the value is not strictly positive.
+     * @throws IllegalArgumentException if the value is not strictly positive 
finite.
      */
     static double checkStrictlyPositive(double v) {
         if (v > 0) {
@@ -132,7 +132,7 @@ final class Arguments {
      * Check that all values are {@code > 0}.
      *
      * @param values Values to be tested.
-     * @throws IllegalArgumentException if any values are not strictly 
positive.
+     * @throws IllegalArgumentException if any values are not strictly 
positive finite.
      */
     static void checkStrictlyPositive(double[] values) {
         for (final double v : values) {
@@ -152,25 +152,39 @@ final class Arguments {
      */
     static double checkFinite(double v) {
         if (!Double.isFinite(v)) {
-            throw new InferenceException("Non-finite input value: " + v);
+            throw new InferenceException(InferenceException.NOT_FINITE, v);
         }
         return v;
     }
 
     /**
-     * Check that all values are not {@link Double#NaN}.
+     * Check that all the values are finite.
      *
      * @param values Values to be tested.
-     * @throws IllegalArgumentException if any values are NaN.
+     * @throws IllegalArgumentException if any values are not finite.
      */
-    static void checkNonNaN(double[] values) {
+    static void checkFinite(double[] values) {
         for (final double v : values) {
-            if (Double.isNaN(v)) {
-                throw new InferenceException("NaN input value");
+            if (!Double.isFinite(v)) {
+                throw new InferenceException(InferenceException.NOT_FINITE, v);
             }
         }
     }
 
+    /**
+     * Check that the value is not {@link Double#NaN}.
+     *
+     * @param v Value to be tested.
+     * @return the value
+     * @throws IllegalArgumentException if the value is NaN.
+     */
+    static double checkNonNaN(double v) {
+        if (Double.isNaN(v)) {
+            throw new InferenceException(InferenceException.NAN);
+        }
+        return v;
+    }
+
     /**
      * Checks if the input array is rectangular. It is assumed the array is 
non-null
      * and has a non-zero length.
diff --git 
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/InferenceException.java
 
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/InferenceException.java
index c3dfd921..fb32f35e 100644
--- 
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/InferenceException.java
+++ 
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/InferenceException.java
@@ -48,6 +48,8 @@ class InferenceException extends IllegalArgumentException {
     static final String INVALID_SIGNIFICANCE = "Not a significance: %s is out 
of range (0, 0.5]";
     /** Error message for "not strictly positive" condition when "{@code x <= 
0}". */
     static final String NOT_STRICTLY_POSITIVE = "Number %s is not greater than 
0";
+    /** Error message for "not finite" condition when "{@code x}" is not 
finite. */
+    static final String NOT_FINITE = "Number %s is not finite";
     /** Error message for "no data" condition. */
     static final String NO_DATA = "No data";
     /** Error message for "too large" condition when "{@code x > y}". */
@@ -56,6 +58,8 @@ class InferenceException extends IllegalArgumentException {
     static final String X_GTE_Y = "%s >= %s";
     /** Error message for "too small" condition when "{@code x < y}". */
     static final String X_LT_Y = "%s < %s";
+    /** Error message for a NaN value. */
+    static final String NAN = "NaN input value";
 
     /** Serializable version identifier. */
     private static final long serialVersionUID = 20221203L;
diff --git 
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/OneWayAnova.java
 
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/OneWayAnova.java
index 6a9610e3..f3292733 100644
--- 
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/OneWayAnova.java
+++ 
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/OneWayAnova.java
@@ -208,8 +208,9 @@ public final class OneWayAnova {
      * @param data Category summary data.
      * @return F statistic
      * @throws IllegalArgumentException if the number of categories is less 
than
-     * two; a contained category does not have at least one value; or all
-     * categories have only one value (zero degrees of freedom within groups)
+     * two; a contained category does not have at least one value; all
+     * categories have only one value (zero degrees of freedom within groups);
+     * or any category contains non-finite values
      */
     public double statistic(Collection<double[]> data) {
         final double[] f = new double[1];
@@ -233,8 +234,9 @@ public final class OneWayAnova {
      * @param data Category summary data.
      * @return test result
      * @throws IllegalArgumentException if the number of categories is less 
than
-     * two; a contained category does not have at least one value; or all
-     * categories have only one value (zero degrees of freedom within groups)
+     * two; a contained category does not have at least one value; all
+     * categories have only one value (zero degrees of freedom within groups);
+     * or any category contains non-finite values
      */
     public Result test(Collection<double[]> data) {
         return aov(data, null);
@@ -252,8 +254,9 @@ public final class OneWayAnova {
      * @param statistic Result for the F statistic (or null).
      * @return test result (or null)
      * @throws IllegalArgumentException if the number of categories is less 
than two; a
-     * contained category does not have at least one value; or all categories 
have only
-     * one value (zero degrees of freedom within groups)
+     * contained category does not have at least one value; all categories 
have only
+     * one value (zero degrees of freedom within groups); or any category 
contains
+     * non-finite values
      */
     private static Result aov(Collection<double[]> data, double[] statistic) {
         Arguments.checkCategoriesRequiredSize(data.size(), 2);
@@ -261,6 +264,7 @@ public final class OneWayAnova {
         for (final double[] array : data) {
             n += array.length;
             Arguments.checkValuesRequiredSize(array.length, 1);
+            Arguments.checkFinite(array);
         }
         final long dfwg = n - data.size();
         if (dfwg == 0) {
diff --git 
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/TTest.java
 
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/TTest.java
index 285bbe78..d16e3823 100644
--- 
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/TTest.java
+++ 
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/TTest.java
@@ -160,11 +160,12 @@ public final class TTest {
      * @param v Sample variance.
      * @param n Sample size.
      * @return t statistic
-     * @throws IllegalArgumentException if the number of samples is {@code < 
2}; or the
-     * variance is negative
+     * @throws IllegalArgumentException if the number of samples is {@code < 
2}; the
+     * variance is negative or NaN; or the mean is NaN
      * @see #withMu(double)
      */
     public double statistic(double m, double v, long n) {
+        Arguments.checkNonNaN(m);
         Arguments.checkNonNegative(v);
         checkSampleSize(n);
         return computeT(m - mu, v, n);
@@ -175,12 +176,14 @@ public final class TTest {
      *
      * @param x Sample values.
      * @return t statistic
-     * @throws IllegalArgumentException if the number of samples is {@code < 2}
+     * @throws IllegalArgumentException if the number of samples is {@code < 
2}; or the
+     * sample contains non-finite values
      * @see #statistic(double, double, long)
      * @see #withMu(double)
      */
     public double statistic(double[] x) {
         final long n = checkSampleSize(x.length);
+        Arguments.checkFinite(x);
         final DoubleStatistics s = DoubleStatistics.of(
             EnumSet.of(Statistic.MEAN, Statistic.VARIANCE), x);
         final double m = s.getAsDouble(Statistic.MEAN);
@@ -200,12 +203,14 @@ public final class TTest {
      * @param x First sample values.
      * @param y Second sample values.
      * @return t statistic
-     * @throws IllegalArgumentException if the number of samples is {@code < 
2}; or the
-     * the size of the samples is not equal
+     * @throws IllegalArgumentException if the number of samples is {@code < 
2}; the
+     * size of the samples is not equal; or the samples contain non-finite 
values
      * @see #withMu(double)
      */
     public double pairedStatistic(double[] x, double[] y) {
         final long n = checkSampleSize(x.length);
+        Arguments.checkFinite(x);
+        Arguments.checkFinite(y);
         final double m = StatisticUtils.meanDifference(x, y);
         final double v = StatisticUtils.varianceDifference(x, y, m);
         return computeT(m - mu, v, n);
@@ -237,12 +242,14 @@ public final class TTest {
      * @param n2 Second sample size.
      * @return t statistic
      * @throws IllegalArgumentException if the number of samples in either 
dataset is
-     * {@code < 2}; or the variances are negative.
+     * {@code < 2}; the variances are negative; or the means are NaN.
      * @see #withMu(double)
      * @see #with(DataDispersion)
      */
     public double statistic(double m1, double v1, long n1,
                             double m2, double v2, long n2) {
+        Arguments.checkNonNaN(m1);
+        Arguments.checkNonNaN(m2);
         Arguments.checkNonNegative(v1);
         Arguments.checkNonNegative(v2);
         checkSampleSize(n1);
@@ -261,13 +268,16 @@ public final class TTest {
      * @param x First sample values.
      * @param y Second sample values.
      * @return t statistic
-     * @throws IllegalArgumentException if the number of samples in either 
dataset is {@code < 2}
+     * @throws IllegalArgumentException if the number of samples in either 
dataset is
+     * {@code < 2}; or the samples contain non-finite values
      * @see #withMu(double)
      * @see #with(DataDispersion)
      */
     public double statistic(double[] x, double[] y) {
         final long n1 = checkSampleSize(x.length);
         final long n2 = checkSampleSize(y.length);
+        Arguments.checkFinite(x);
+        Arguments.checkFinite(y);
         final DoubleStatistics.Builder b = 
DoubleStatistics.builder(Statistic.MEAN, Statistic.VARIANCE);
         final DoubleStatistics s1 = b.build(x);
         final double m1 = s1.getAsDouble(Statistic.MEAN);
@@ -386,7 +396,7 @@ public final class TTest {
      * @param y Second sample values.
      * @return the test result
      * @throws IllegalArgumentException if the number of samples in either 
dataset
-     * is {@code < 2}
+     * is {@code < 2}; or the samples contain non-finite values
      * @see #statistic(double[], double[])
      * @see #test(double, double, long, double, double, long)
      */
@@ -395,6 +405,8 @@ public final class TTest {
         // requires the variance. So repeat the computation and compute p.
         final long n1 = checkSampleSize(x.length);
         final long n2 = checkSampleSize(y.length);
+        Arguments.checkFinite(x);
+        Arguments.checkFinite(y);
         final DoubleStatistics.Builder b = 
DoubleStatistics.builder(Statistic.MEAN, Statistic.VARIANCE);
         final DoubleStatistics s1 = b.build(x);
         final double m1 = s1.getAsDouble(Statistic.MEAN);
diff --git 
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/ArgumentsTest.java
 
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/ArgumentsTest.java
index 6b27358b..8dbc0a22 100644
--- 
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/ArgumentsTest.java
+++ 
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/ArgumentsTest.java
@@ -95,20 +95,28 @@ class ArgumentsTest {
         Assertions.assertTrue(ex.getMessage().contains(Double.toString(v)));
     }
 
-    @Test
-    void testCheckNonNanArrayThrows() {
-        Assertions.assertDoesNotThrow(() -> Arguments.checkNonNaN(new 
double[0]));
+    @ParameterizedTest
+    @ValueSource(doubles = {Double.NEGATIVE_INFINITY, Double.NaN, 
Double.POSITIVE_INFINITY})
+    void testCheckFiniteArrayThrows(double v) {
+        Assertions.assertDoesNotThrow(() -> Arguments.checkFinite(new 
double[0]));
         final double[] a = new double[3];
-        Assertions.assertDoesNotThrow(() -> Arguments.checkNonNaN(a));
+        Assertions.assertDoesNotThrow(() -> Arguments.checkFinite(a));
         for (int i = 0; i < a.length; i++) {
-            a[i] = Double.NaN;
+            a[i] = v;
             final IllegalArgumentException ex = 
Assertions.assertThrows(IllegalArgumentException.class,
-                () -> Arguments.checkNonNaN(a));
-            Assertions.assertTrue(ex.getMessage().contains("NaN"));
+                () -> Arguments.checkFinite(a));
+            
Assertions.assertTrue(ex.getMessage().contains(Double.toString(v)));
             a[i] = 0;
         }
     }
 
+    @Test
+    void testCheckNonNanThrows() {
+        final IllegalArgumentException ex = 
Assertions.assertThrows(IllegalArgumentException.class,
+            () -> Arguments.checkNonNaN(Double.NaN));
+        Assertions.assertTrue(ex.getMessage().contains("NaN"));
+    }
+
     @Test
     void testCheckRectangular() {
         // Input is assumed to be non-zero length: this test what happens
diff --git 
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/OneWayAnovaTest.java
 
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/OneWayAnovaTest.java
index 6e3ec255..190c57d2 100644
--- 
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/OneWayAnovaTest.java
+++ 
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/OneWayAnovaTest.java
@@ -54,6 +54,12 @@ class OneWayAnovaTest {
         final List<double[]> allLength1 = Arrays.asList(new double[] {1}, new 
double[] {2}, new double[] {3});
         TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
             () -> action.accept(allLength1), "degrees", "freedom", "within", 
"group", "zero");
+
+        for (final double v : new double[] {Double.NaN, 
Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}) {
+            final List<double[]> nanContents = Arrays.asList(new double[] {1, 
2, 3}, new double[] {4, v});
+            TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
+                () -> action.accept(nanContents), "finite", 
Double.toString(v));
+        }
     }
 
     @ParameterizedTest
diff --git 
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/TTestTest.java
 
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/TTestTest.java
index bbb1dcaf..376e1a61 100644
--- 
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/TTestTest.java
+++ 
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/TTestTest.java
@@ -67,6 +67,10 @@ class TTestTest {
             () -> action.accept(m, v, 1), "values", "size");
         TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
             () -> action.accept(m, -1, n), "negative");
+        TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
+            () -> action.accept(m, Double.NaN, n), "NaN");
+        TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
+            () -> action.accept(Double.NaN, v, n), "NaN");
     }
 
     @ParameterizedTest
@@ -120,6 +124,12 @@ class TTestTest {
     private static void assertOneSampleThrows(Consumer<double[]> action) {
         TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
             () -> action.accept(new double[1]), "values", "size");
+
+        for (final double v : new double[] {Double.NaN, 
Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}) {
+            final double[] badSample = {1, 2, 3, v};
+            TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
+                () -> action.accept(badSample), "finite", Double.toString(v));
+        }
     }
 
     @ParameterizedTest
@@ -182,6 +192,13 @@ class TTestTest {
             () -> action.accept(sample, unequalSize), "values", "size", 
"mismatch");
         TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
             () -> action.accept(unequalSize, sample), "values", "size", 
"mismatch");
+        for (final double v : new double[] {Double.NaN, 
Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}) {
+            final double[] badSample = {1, 2, 3, v};
+            TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
+                () -> action.accept(badSample, sample), "finite", 
Double.toString(v));
+            TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
+                () -> action.accept(sample, badSample), "finite", 
Double.toString(v));
+        }
     }
 
     @ParameterizedTest
@@ -254,6 +271,14 @@ class TTestTest {
             () -> action.accept(m, -1, n, m, v, n), "negative");
         TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
             () -> action.accept(m, v, n, m, -1, n), "negative");
+        TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
+            () -> action.accept(Double.NaN, v, n, m, v, n), "NaN");
+        TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
+            () -> action.accept(m, Double.NaN, n, m, v, n), "NaN");
+        TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
+            () -> action.accept(m, v, n, Double.NaN, v, n), "NaN");
+        TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
+            () -> action.accept(m, v, n, m, Double.NaN, n), "NaN");
     }
 
     @ParameterizedTest
@@ -344,6 +369,13 @@ class TTestTest {
             () -> action.accept(sample, tooSmall), "values", "size");
         TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
             () -> action.accept(tooSmall, sample), "values", "size");
+        for (final double v : new double[] {Double.NaN, 
Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}) {
+            final double[] badSample = {1, 2, 3, v};
+            TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
+                () -> action.accept(badSample, sample), "finite", 
Double.toString(v));
+            TestUtils.assertThrowsWithMessage(IllegalArgumentException.class,
+                () -> action.accept(sample, badSample), "finite", 
Double.toString(v));
+        }
     }
 
     @ParameterizedTest
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d8aa96ad..390ba746 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -53,6 +53,10 @@ If the output is not quite correct, check for invisible 
trailing spaces!
   </properties>
   <body>
     <release version="1.4" date="TBD" description="Adds new features (requires 
Java 8).">
+      <action dev="aherbert" type="fix" due-to="Security scan, Aex Herbert" 
issue="STATISTICS-97">
+        "TTest/OneWayAnova": Checks added for arguments that invalidate the 
test
+        statistic: non-finite values in input arrays; and NaN mean arguments.
+      </action>
       <action dev="aherbert" type="update" due-to="Security scan, Aex Herbert">
         "FisherExactTest/HypergeometricDistribution": Update documentation
         on possible long runtime when the hypergeometric distribution

Reply via email to