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

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

commit 780f790c5936e00aeff28c637c7b794497a8bbb2
Author: aherbert <aherb...@apache.org>
AuthorDate: Fri Jul 30 15:55:41 2021 +0100

    Consistent ordering of abstract tests
    
    Ensure ContinuousDistributionAbstractTest and
    DiscreteDistributionAbstractTest are as close as possible given the
    interfaces that they must test.
    
    Updated the names for the probability precondition tests.
---
 .../ConstantContinuousDistributionTest.java        |   2 +-
 .../ContinuousDistributionAbstractTest.java        | 399 ++++++++++++---------
 .../DiscreteDistributionAbstractTest.java          | 337 +++++++++--------
 .../TruncatedNormalDistributionTest.java           |   8 +-
 4 files changed, 429 insertions(+), 317 deletions(-)

diff --git 
a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/ConstantContinuousDistributionTest.java
 
b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/ConstantContinuousDistributionTest.java
index 7552c76..be882c0 100644
--- 
a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/ConstantContinuousDistributionTest.java
+++ 
b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/ConstantContinuousDistributionTest.java
@@ -77,7 +77,7 @@ class ConstantContinuousDistributionTest extends 
ContinuousDistributionAbstractT
 
     @Test
     @Override
-    void testSampler() {
+    void testSampling() {
         final double value = 12.345;
         final ContinuousDistribution.Sampler sampler = new 
ConstantContinuousDistribution(value).createSampler(null);
         for (int i = 0; i < 10; i++) {
diff --git 
a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/ContinuousDistributionAbstractTest.java
 
b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/ContinuousDistributionAbstractTest.java
index 6c5efff..0180328 100644
--- 
a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/ContinuousDistributionAbstractTest.java
+++ 
b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/ContinuousDistributionAbstractTest.java
@@ -17,6 +17,7 @@
 package org.apache.commons.statistics.distribution;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 
 import org.apache.commons.math3.analysis.UnivariateFunction;
@@ -39,12 +40,13 @@ import org.junit.jupiter.api.BeforeEach;
  * distribution returned by makeDistribution().  Default implementations
  * are provided for the makeInverseXxx methods that just invert the mapping
  * defined by the arrays returned by the makeCumulativeXxx methods.
- * <p>
- * makeCumulativeTestPoints() -- arguments used to test cumulative 
probabilities
- * makeCumulativeTestValues() -- expected cumulative probabilities
- * makeDensityTestValues() -- expected density values at cumulativeTestPoints
- * makeInverseCumulativeTestPoints() -- arguments used to test inverse cdf
- * makeInverseCumulativeTestValues() -- expected inverse cdf values
+ * <ul>
+ *  <li>makeCumulativeTestPoints() -- arguments used to test cumulative 
probabilities
+ *  <li>makeCumulativeTestValues() -- expected cumulative probabilities
+ *  <li>makeDensityTestValues() -- expected density values at 
cumulativeTestPoints
+ *  <li>makeInverseCumulativeTestPoints() -- arguments used to test inverse 
cdf evaluation
+ *  <li>makeInverseCumulativeTestValues() -- expected inverse cdf values
+ * </ul>
  * <p>
  * If the continuous distribution provides higher precision implementations of 
cumulativeProbability
  * and/or survivalProbability, the following methods should be implemented to 
provide testing.
@@ -53,12 +55,12 @@ import org.junit.jupiter.api.BeforeEach;
  * arithmetic.
  *
  * NOTE: The default high-precision threshold is 1e-22.
- * <pre>
- * makeCumulativePrecisionTestPoints() -- high precision test inputs
- * makeCumulativePrecisionTestValues() -- high precision expected results
- * makeSurvivalPrecisionTestPoints() -- high precision test inputs
- * makeSurvivalPrecisionTestValues() -- high precision expected results
- * </pre>
+ * <ul>
+ *  <li>makeCumulativePrecisionTestPoints() -- high precision test inputs
+ *  <li>makeCumulativePrecisionTestValues() -- high precision expected results
+ *  <li>makeSurvivalPrecisionTestPoints() -- high precision test inputs
+ *  <li>makeSurvivalPrecisionTestValues() -- high precision expected results
+ * </ul>
  * <p>
  * To implement additional test cases with different distribution instances and
  * test data, use the setXxx methods for the instance data in test cases and
@@ -76,7 +78,8 @@ import org.junit.jupiter.api.BeforeEach;
  */
 abstract class ContinuousDistributionAbstractTest {
 
-//-------------------- Private test instance data -------------------------
+    //-------------------- Private test instance data -------------------------
+
     /**  Distribution instance used to perform tests. */
     private ContinuousDistribution distribution;
 
@@ -84,7 +87,17 @@ abstract class ContinuousDistributionAbstractTest {
     private double tolerance = 1e-4;
 
     /** Tolerance used in high precision tests. */
-    private final double highPrecisionTolerance = 1e-22;
+    private double highPrecisionTolerance = 1e-22;
+
+    // Note:
+    // The ContinuousDistribution interface defines the density as the 
gradient of the CDF.
+    // It is evaluated using the cumulativeTestPoints.
+
+    /** Values used to test density calculations. */
+    private double[] densityTestValues;
+
+    /** Values used to test logarithmic density calculations. */
+    private double[] logDensityTestValues;
 
     /** Arguments used to test cumulative probability density calculations. */
     private double[] cumulativeTestPoints;
@@ -110,17 +123,25 @@ abstract class ContinuousDistributionAbstractTest {
     /** Values used to test inverse cumulative probability density 
calculations. */
     private double[] inverseCumulativeTestValues;
 
-    /** Values used to test density calculations. */
-    private double[] densityTestValues;
-
-    /** Values used to test logarithmic density calculations. */
-    private double[] logDensityTestValues;
-
     //-------------------- Abstract methods -----------------------------------
 
     /** Creates the default continuous distribution instance to use in tests. 
*/
     public abstract ContinuousDistribution makeDistribution();
 
+    /** Creates the default density test expected values. */
+    public abstract double[] makeDensityTestValues();
+
+    /** Creates the default logarithmic probability density test expected 
values.
+     *
+     * <p>The default implementation simply computes the logarithm of all the 
values in
+     * {@link #makeDensityTestValues()}.
+     *
+     * @return the default logarithmic probability density test expected 
values.
+     */
+    public double[] makeLogDensityTestValues() {
+        return Arrays.stream(makeDensityTestValues()).map(Math::log).toArray();
+    }
+
     /** Creates the default cumulative probability test input values. */
     public abstract double[] makeCumulativeTestPoints();
 
@@ -155,21 +176,6 @@ abstract class ContinuousDistributionAbstractTest {
         return new double[0];
     }
 
-    /** Creates the default density test expected values. */
-    public abstract double[] makeDensityTestValues();
-
-    /** Creates the default logarithmic density test expected values.
-     * The default implementation simply computes the logarithm
-     * of each value returned by {@link #makeDensityTestValues()}.*/
-    public double[] makeLogDensityTestValues() {
-        final double[] density = makeDensityTestValues();
-        final double[] logDensity = new double[density.length];
-        for (int i = 0; i < density.length; i++) {
-            logDensity[i] = Math.log(density[i]);
-        }
-        return logDensity;
-    }
-
     //---- Default implementations of inverse test data generation methods ----
 
     /** Creates the default inverse cumulative probability test input values. 
*/
@@ -193,6 +199,8 @@ abstract class ContinuousDistributionAbstractTest {
     @BeforeEach
     void setUp() {
         distribution = makeDistribution();
+        densityTestValues = makeDensityTestValues();
+        logDensityTestValues = makeLogDensityTestValues();
         cumulativeTestPoints = makeCumulativeTestPoints();
         cumulativeTestValues = makeCumulativeTestValues();
         cumulativePrecisionTestPoints = makeCumulativePrecisionTestPoints();
@@ -201,8 +209,6 @@ abstract class ContinuousDistributionAbstractTest {
         survivalPrecisionTestValues = makeSurvivalPrecisionTestValues();
         inverseCumulativeTestPoints = makeInverseCumulativeTestPoints();
         inverseCumulativeTestValues = makeInverseCumulativeTestValues();
-        densityTestValues = makeDensityTestValues();
-        logDensityTestValues = makeLogDensityTestValues();
     }
 
     /**
@@ -211,17 +217,47 @@ abstract class ContinuousDistributionAbstractTest {
     @AfterEach
     void tearDown() {
         distribution = null;
+        densityTestValues = null;
+        logDensityTestValues = null;
         cumulativeTestPoints = null;
         cumulativeTestValues = null;
+        cumulativePrecisionTestPoints = null;
+        cumulativePrecisionTestValues = null;
+        survivalPrecisionTestPoints = null;
+        survivalPrecisionTestValues = null;
         inverseCumulativeTestPoints = null;
         inverseCumulativeTestValues = null;
-        densityTestValues = null;
-        logDensityTestValues = null;
     }
 
     //-------------------- Verification methods -------------------------------
 
     /**
+     * Verifies that density calculations match expected values
+     * using current test instance data.
+     */
+    protected void verifyDensities() {
+        for (int i = 0; i < cumulativeTestPoints.length; i++) {
+            final double x = cumulativeTestPoints[i];
+            Assertions.assertEquals(densityTestValues[i],
+                distribution.density(x), getTolerance(),
+                () -> "Incorrect probability density value returned for " + x);
+        }
+    }
+
+    /**
+     * Verifies that logarithmic density calculations match expected values
+     * using current test instance data.
+     */
+    protected void verifyLogDensities() {
+        for (int i = 0; i < cumulativeTestPoints.length; i++) {
+            final double x = cumulativeTestPoints[i];
+            Assertions.assertEquals(logDensityTestValues[i],
+                distribution.logDensity(x), getTolerance(),
+                () -> "Incorrect probability density value returned for " + x);
+        }
+    }
+
+    /**
      * Verifies that cumulative probability density calculations match 
expected values
      * using current test instance data.
      */
@@ -229,9 +265,8 @@ abstract class ContinuousDistributionAbstractTest {
         // verify cumulativeProbability(double)
         for (int i = 0; i < cumulativeTestPoints.length; i++) {
             final double x = cumulativeTestPoints[i];
-            Assertions.assertEquals(
-                cumulativeTestValues[i],
-                distribution.cumulativeProbability(cumulativeTestPoints[i]),
+            Assertions.assertEquals(cumulativeTestValues[i],
+                distribution.cumulativeProbability(x),
                 getTolerance(),
                 () -> "Incorrect cumulative probability value returned for " + 
x);
         }
@@ -272,7 +307,7 @@ abstract class ContinuousDistributionAbstractTest {
                 1.0,
                 distribution.survivalProbability(x) + 
distribution.cumulativeProbability(x),
                 getTolerance(),
-                () -> "survival + cumulative probability were not close to 
1.0" + x);
+                () -> "survival + cumulative probability were not close to 1.0 
for " + x);
         }
     }
 
@@ -321,40 +356,18 @@ abstract class ContinuousDistributionAbstractTest {
         }
     }
 
-    /**
-     * Verifies that density calculations match expected values.
-     */
-    protected void verifyDensities() {
-        for (int i = 0; i < cumulativeTestPoints.length; i++) {
-            final double x = cumulativeTestPoints[i];
-            Assertions.assertEquals(
-                densityTestValues[i],
-                distribution.density(cumulativeTestPoints[i]),
-                getTolerance(),
-                () -> "Incorrect probability density value returned for " + x);
-        }
-    }
+    //------------------------ Default test cases -----------------------------
 
-    /**
-     * Verifies that logarithmic density calculations match expected values.
-     */
-    protected void verifyLogDensities() {
-        for (int i = 0; i < cumulativeTestPoints.length; i++) {
-            final double x = cumulativeTestPoints[i];
-            Assertions.assertEquals(
-                logDensityTestValues[i],
-                distribution.logDensity(cumulativeTestPoints[i]),
-                getTolerance(),
-                () -> "Incorrect probability density value returned for " + x);
-        }
+    @Test
+    void testDensities() {
+        verifyDensities();
     }
 
-    //------------------------ Default test cases -----------------------------
+    @Test
+    void testLogDensities() {
+        verifyLogDensities();
+    }
 
-    /**
-     * Verifies that cumulative probability density calculations match 
expected values
-     * using default test instance data.
-     */
     @Test
     void testCumulativeProbabilities() {
         verifyCumulativeProbabilities();
@@ -380,34 +393,12 @@ abstract class ContinuousDistributionAbstractTest {
         verifySurvivalProbabilityPrecision();
     }
 
-    /**
-     * Verifies that inverse cumulative probability density calculations match 
expected values
-     * using default test instance data.
-     */
     @Test
     void testInverseCumulativeProbabilities() {
         verifyInverseCumulativeProbabilities();
     }
 
     /**
-     * Verifies that density calculations return expected values
-     * for default test instance data.
-     */
-    @Test
-    void testDensities() {
-        verifyDensities();
-    }
-
-    /**
-     * Verifies that logarithmic density calculations return expected values
-     * for default test instance data.
-     */
-    @Test
-    void testLogDensities() {
-        verifyLogDensities();
-    }
-
-    /**
      * Verifies that probability computations are consistent.
      */
     @Test
@@ -416,9 +407,9 @@ abstract class ContinuousDistributionAbstractTest {
 
             // check that cdf(x, x) = 0
             Assertions.assertEquals(
-                0d,
+                0.0,
                 distribution.probability(cumulativeTestPoints[i], 
cumulativeTestPoints[i]),
-                tolerance);
+                getTolerance());
 
             // check that P(a < X <= b) = P(X <= b) - P(X <= a)
             final double upper = Math.max(cumulativeTestPoints[i], 
cumulativeTestPoints[i - 1]);
@@ -426,61 +417,65 @@ abstract class ContinuousDistributionAbstractTest {
             final double diff = distribution.cumulativeProbability(upper) -
                 distribution.cumulativeProbability(lower);
             final double direct = distribution.probability(lower, upper);
-            Assertions.assertEquals(diff, direct, tolerance,
+            Assertions.assertEquals(diff, direct, getTolerance(),
                 () -> "Inconsistent probability for (" + lower + "," + upper + 
")");
         }
     }
 
-    /**
-     * Verifies that illegal arguments are correctly handled
-     */
     @Test
-    void testPrecondition1() {
-        Assertions.assertThrows(DistributionException.class, () -> 
distribution.probability(1, 0));
+    void testOutsideSupport() {
+        // Test various quantities when the variable is outside the support.
+        final double lo = distribution.getSupportLowerBound();
+        Assertions.assertEquals(lo, 
distribution.inverseCumulativeProbability(0.0));
+
+        final double below = Math.nextDown(lo);
+        Assertions.assertEquals(0.0, distribution.probability(below));
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, 
distribution.logDensity(below));
+        Assertions.assertEquals(0.0, 
distribution.cumulativeProbability(below));
+        Assertions.assertEquals(1.0, distribution.survivalProbability(below));
+
+        final double hi = distribution.getSupportUpperBound();
+        Assertions.assertEquals(0.0, distribution.survivalProbability(hi));
+        Assertions.assertEquals(hi, 
distribution.inverseCumulativeProbability(1.0));
+
+        final double above = Math.nextUp(hi);
+        Assertions.assertEquals(0.0, distribution.probability(above));
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, 
distribution.logDensity(above));
+        Assertions.assertEquals(1.0, 
distribution.cumulativeProbability(above));
+        Assertions.assertEquals(0.0, distribution.survivalProbability(above));
     }
+
     @Test
-    void testPrecondition2() {
-        Assertions.assertThrows(DistributionException.class, () -> 
distribution.inverseCumulativeProbability(-1));
+    void testProbabilityWithLowerBoundAboveUpperBound() {
+        Assertions.assertThrows(DistributionException.class, () -> 
distribution.probability(1, 0));
     }
+
     @Test
-    void testPrecondition3() {
-        Assertions.assertThrows(DistributionException.class, () -> 
distribution.inverseCumulativeProbability(2));
+    void testInverseCumulativeProbabilityWithProbabilityBelowZero() {
+        Assertions.assertThrows(DistributionException.class, () -> 
distribution.inverseCumulativeProbability(-1));
     }
 
     @Test
-    void testOutsideSupport() {
-        // Test various quantities when the variable is outside the support.
-        final double lo = distribution.getSupportLowerBound();
-        final double hi = distribution.getSupportUpperBound();
-        final double below = lo - Math.ulp(lo);
-        final double above = hi + Math.ulp(hi);
-
-        Assertions.assertEquals(0d, distribution.density(below));
-        Assertions.assertEquals(0d, distribution.density(above));
-        Assertions.assertEquals(Double.NEGATIVE_INFINITY, 
distribution.logDensity(below));
-        Assertions.assertEquals(Double.NEGATIVE_INFINITY, 
distribution.logDensity(above));
-        Assertions.assertEquals(0d, distribution.cumulativeProbability(below));
-        Assertions.assertEquals(1d, distribution.cumulativeProbability(above));
-        Assertions.assertEquals(1d, distribution.survivalProbability(below));
-        Assertions.assertEquals(0d, distribution.survivalProbability(above));
+    void testInverseCumulativeProbabilityWithProbabilityAboveOne() {
+        Assertions.assertThrows(DistributionException.class, () -> 
distribution.inverseCumulativeProbability(2));
     }
 
-    /**
-     * Test sampling.
-     */
     @Test
-    void testSampler() {
+    void testSampling() {
+        // Use fixed seed.
         final int sampleSize = 1000;
         final ContinuousDistribution.Sampler sampler =
-            
distribution.createSampler(RandomSource.create(RandomSource.WELL_19937_C, 
123456789L));
+            
getDistribution().createSampler(RandomSource.create(RandomSource.WELL_19937_C, 
123456789L));
         final double[] sample = TestUtils.sample(sampleSize, sampler);
-        final double[] quartiles = 
TestUtils.getDistributionQuartiles(distribution);
+
+        final double[] quartiles = 
TestUtils.getDistributionQuartiles(getDistribution());
         final double[] expected = {250, 250, 250, 250};
-        final long[] counts = new long[4];
 
+        final long[] counts = new long[4];
         for (int i = 0; i < sampleSize; i++) {
             TestUtils.updateCounts(sample[i], counts, quartiles);
         }
+
         TestUtils.assertChiSquareAccept(expected, counts, 0.001);
     }
 
@@ -537,32 +532,47 @@ abstract class ContinuousDistributionAbstractTest {
     }
 
     //------------------ Getters / Setters for test instance data -----------
+
     /**
-     * @return Returns the cumulativeTestPoints.
+     * @return Returns the distribution.
      */
-    protected double[] getCumulativeTestPoints() {
-        return cumulativeTestPoints;
+    protected ContinuousDistribution getDistribution() {
+        return distribution;
     }
 
     /**
-     * @param cumulativeTestPoints The cumulativeTestPoints to set.
+     * @param distribution The distribution to set.
      */
-    protected void setCumulativeTestPoints(double[] cumulativeTestPoints) {
-        this.cumulativeTestPoints = cumulativeTestPoints;
+    protected void setDistribution(ContinuousDistribution distribution) {
+        this.distribution = distribution;
     }
 
     /**
-     * @return Returns the cumulativeTestValues.
+     * @return Returns the tolerance.
      */
-    protected double[] getCumulativeTestValues() {
-        return cumulativeTestValues;
+    protected double getTolerance() {
+        return tolerance;
     }
 
     /**
-     * @param cumulativeTestValues The cumulativeTestValues to set.
+     * @param tolerance The tolerance to set.
      */
-    protected void setCumulativeTestValues(double[] cumulativeTestValues) {
-        this.cumulativeTestValues = cumulativeTestValues;
+    protected void setTolerance(double tolerance) {
+        this.tolerance = tolerance;
+    }
+
+    /**
+     * @return Returns the high precision tolerance.
+     */
+    protected double getHighPrecisionTolerance() {
+        return highPrecisionTolerance;
+    }
+
+    /**
+     * @param highPrecisionTolerance The high precision highPrecisionTolerance 
to set.
+     */
+    protected void setHighPrecisionTolerance(double highPrecisionTolerance) {
+        this.highPrecisionTolerance = highPrecisionTolerance;
     }
 
     /**
@@ -573,10 +583,14 @@ abstract class ContinuousDistributionAbstractTest {
     }
 
     /**
+     * Set the density test values.
+     * For convenience this recomputes the log density test values using 
{@link Math#log(double)}.
+     *
      * @param densityTestValues The densityTestValues to set.
      */
     protected void setDensityTestValues(double[] densityTestValues) {
         this.densityTestValues = densityTestValues;
+        logDensityTestValues = 
Arrays.stream(densityTestValues).map(Math::log).toArray();
     }
 
     /**
@@ -594,66 +608,115 @@ abstract class ContinuousDistributionAbstractTest {
     }
 
     /**
-     * @return Returns the distribution.
+     * @return Returns the cumulativeTestPoints.
      */
-    protected ContinuousDistribution getDistribution() {
-        return distribution;
+    protected double[] getCumulativeTestPoints() {
+        return cumulativeTestPoints;
     }
 
     /**
-     * @param distribution The distribution to set.
+     * @param cumulativeTestPoints The cumulativeTestPoints to set.
      */
-    protected void setDistribution(ContinuousDistribution distribution) {
-        this.distribution = distribution;
+    protected void setCumulativeTestPoints(double[] cumulativeTestPoints) {
+        this.cumulativeTestPoints = cumulativeTestPoints;
     }
 
     /**
-     * @return Returns the inverseCumulativeTestPoints.
+     * @return Returns the cumulativeTestValues.
      */
-    protected double[] getInverseCumulativeTestPoints() {
-        return inverseCumulativeTestPoints;
+    protected double[] getCumulativeTestValues() {
+        return cumulativeTestValues;
     }
 
     /**
-     * @param inverseCumulativeTestPoints The inverseCumulativeTestPoints to 
set.
+     * @param cumulativeTestValues The cumulativeTestValues to set.
      */
-    protected void setInverseCumulativeTestPoints(double[] 
inverseCumulativeTestPoints) {
-        this.inverseCumulativeTestPoints = inverseCumulativeTestPoints;
+    protected void setCumulativeTestValues(double[] cumulativeTestValues) {
+        this.cumulativeTestValues = cumulativeTestValues;
     }
 
     /**
-     * @return Returns the inverseCumulativeTestValues.
+     * @return Returns the cumulativePrecisionTestPoints.
      */
-    protected double[] getInverseCumulativeTestValues() {
-        return inverseCumulativeTestValues;
+    protected double[] getCumulativePrecisionTestPoints() {
+        return cumulativePrecisionTestPoints;
     }
 
     /**
-     * @param inverseCumulativeTestValues The inverseCumulativeTestValues to 
set.
+     * @param cumulativePrecisionTestPoints The cumulativePrecisionTestPoints 
to set.
      */
-    protected void setInverseCumulativeTestValues(double[] 
inverseCumulativeTestValues) {
-        this.inverseCumulativeTestValues = inverseCumulativeTestValues;
+    protected void setCumulativePrecisionTestPoints(double[] 
cumulativePrecisionTestPoints) {
+        this.cumulativePrecisionTestPoints = cumulativePrecisionTestPoints;
     }
 
     /**
-     * @return Returns the tolerance.
+     * @return Returns the cumulativePrecisionTestValues.
      */
-    protected double getTolerance() {
-        return tolerance;
+    protected double[] getCumulativePrecisionTestValues() {
+        return cumulativePrecisionTestValues;
     }
 
     /**
-     * @return Returns the high precision tolerance.
+     * @param cumulativePrecisionTestValues The cumulativePrecisionTestValues 
to set.
      */
-    protected double getHighPrecisionTolerance() {
-        return highPrecisionTolerance;
+    protected void setCumulativePrecisionTestValues(double[] 
cumulativePrecisionTestValues) {
+        this.cumulativePrecisionTestValues = cumulativePrecisionTestValues;
     }
 
     /**
-     * @param tolerance The tolerance to set.
+     * @return Returns the survivalPrecisionTestPoints.
      */
-    protected void setTolerance(double tolerance) {
-        this.tolerance = tolerance;
+    protected double[] getSurvivalPrecisionTestPoints() {
+        return survivalPrecisionTestPoints;
+    }
+
+    /**
+     * @param survivalPrecisionTestPoints The survivalPrecisionTestPoints to 
set.
+     */
+    protected void setSurvivalPrecisionTestPoints(double[] 
survivalPrecisionTestPoints) {
+        this.survivalPrecisionTestPoints = survivalPrecisionTestPoints;
+    }
+
+    /**
+     * @return Returns the survivalPrecisionTestValues.
+     */
+    protected double[] getSurvivalPrecisionTestValues() {
+        return survivalPrecisionTestValues;
+    }
+
+    /**
+     * @param survivalPrecisionTestValues The survivalPrecisionTestValues to 
set.
+     */
+    protected void setSurvivalPrecisionTestValues(double[] 
survivalPrecisionTestValues) {
+        this.survivalPrecisionTestValues = survivalPrecisionTestValues;
+    }
+
+    /**
+     * @return Returns the inverseCumulativeTestPoints.
+     */
+    protected double[] getInverseCumulativeTestPoints() {
+        return inverseCumulativeTestPoints;
+    }
+
+    /**
+     * @param inverseCumulativeTestPoints The inverseCumulativeTestPoints to 
set.
+     */
+    protected void setInverseCumulativeTestPoints(double[] 
inverseCumulativeTestPoints) {
+        this.inverseCumulativeTestPoints = inverseCumulativeTestPoints;
+    }
+
+    /**
+     * @return Returns the inverseCumulativeTestValues.
+     */
+    protected double[] getInverseCumulativeTestValues() {
+        return inverseCumulativeTestValues;
+    }
+
+    /**
+     * @param inverseCumulativeTestValues The inverseCumulativeTestValues to 
set.
+     */
+    protected void setInverseCumulativeTestValues(double[] 
inverseCumulativeTestValues) {
+        this.inverseCumulativeTestValues = inverseCumulativeTestValues;
     }
 
     /**
diff --git 
a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/DiscreteDistributionAbstractTest.java
 
b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/DiscreteDistributionAbstractTest.java
index 0df01dd..f96c3e2 100644
--- 
a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/DiscreteDistributionAbstractTest.java
+++ 
b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/DiscreteDistributionAbstractTest.java
@@ -26,18 +26,22 @@ import org.junit.jupiter.api.Test;
 /**
  * Abstract base class for {@link DiscreteDistribution} tests.
  * <p>
- * To create a concrete test class for an integer distribution implementation,
- *  implement makeDistribution() to return a distribution instance to use in
- *  tests and each of the test data generation methods below.  In each case, 
the
- *  test points and test values arrays returned represent parallel arrays of
- *  inputs and expected values for the distribution returned by 
makeDistribution().
- *  <p>
- *  makeDensityTestPoints() -- arguments used to test probability density 
calculation
- *  makeDensityTestValues() -- expected probability densities
- *  makeCumulativeTestPoints() -- arguments used to test cumulative 
probabilities
- *  makeCumulativeTestValues() -- expected cumulative probabilites
- *  makeInverseCumulativeTestPoints() -- arguments used to test inverse cdf 
evaluation
- *  makeInverseCumulativeTestValues() -- expected inverse cdf values
+ * To create a concrete test class for a continuous distribution
+ * implementation, first implement makeDistribution() to return a distribution
+ * instance to use in tests. Then implement each of the test data generation
+ * methods below.  In each case, the test points and test values arrays
+ * returned represent parallel arrays of inputs and expected values for the
+ * distribution returned by makeDistribution().  Default implementations
+ * are provided for the makeInverseXxx methods that just invert the mapping
+ * defined by the arrays returned by the makeCumulativeXxx methods.
+ * <ul>
+ *  <li>makeDensityTestPoints() -- arguments used to test probability density 
calculation
+ *  <li>makeDensityTestValues() -- expected probability densities
+ *  <li>makeCumulativeTestPoints() -- arguments used to test cumulative 
probabilities
+ *  <li>makeCumulativeTestValues() -- expected cumulative probabilities
+ *  <li>makeInverseCumulativeTestPoints() -- arguments used to test inverse 
cdf evaluation
+ *  <li>makeInverseCumulativeTestValues() -- expected inverse cdf values
+ * </ul>
  * <p>
  * If the discrete distribution provides higher precision implementations of 
cumulativeProbability
  * and/or survivalProbability, the following methods should be implemented to 
provide testing.
@@ -46,20 +50,31 @@ import org.junit.jupiter.api.Test;
  * arithmetic.
  *
  * NOTE: The default high-precision threshold is 1e-22.
- * <pre>
- * makeCumulativePrecisionTestPoints() -- high precision test inputs
- * makeCumulativePrecisionTestValues() -- high precision expected results
- * makeSurvivalPrecisionTestPoints() -- high precision test inputs
- * makeSurvivalPrecisionTestValues() -- high precision expected results
- * </pre>
+ * <ul>
+ *  <li>makeCumulativePrecisionTestPoints() -- high precision test inputs
+ *  <li>makeCumulativePrecisionTestValues() -- high precision expected results
+ *  <li>makeSurvivalPrecisionTestPoints() -- high precision test inputs
+ *  <li>makeSurvivalPrecisionTestValues() -- high precision expected results
+ * </ul>
  * <p>
- *  To implement additional test cases with different distribution instances 
and test data,
- *  use the setXxx methods for the instance data in test cases and call the 
verifyXxx methods
- *  to verify results.
+ * To implement additional test cases with different distribution instances and
+ * test data, use the setXxx methods for the instance data in test cases and
+ * call the verifyXxx methods to verify results.
+ * <p>
+ * Error tolerance can be overridden by implementing getTolerance().
+ * <p>
+ * Test data should be validated against reference tables or other packages
+ * where possible, and the source of the reference data and/or validation
+ * should be documented in the test cases.  A framework for validating
+ * distribution data against R is included in the /src/test/R source tree.
+ * <p>
+ * See {@link PoissonDistributionTest} and {@link PascalDistributionTest}
+ * for examples.
  */
 abstract class DiscreteDistributionAbstractTest {
 
-//-------------------- Private test instance data -------------------------
+    //-------------------- Private test instance data -------------------------
+
     /** Discrete distribution instance used to perform tests. */
     private DiscreteDistribution distribution;
 
@@ -115,19 +130,19 @@ abstract class DiscreteDistributionAbstractTest {
 
     /** Creates the default logarithmic probability density test expected 
values.
      *
-     * The default implementation simply computes the logarithm of all the 
values in
+     * <p>The default implementation simply computes the logarithm of all the 
values in
      * {@link #makeDensityTestValues()}.
      *
-     * @return double[] the default logarithmic probability density test 
expected values.
+     * @return the default logarithmic probability density test expected 
values.
      */
     public double[] makeLogDensityTestValues() {
         return Arrays.stream(makeDensityTestValues()).map(Math::log).toArray();
     }
 
-    /** Creates the default cumulative probability density test input values. 
*/
+    /** Creates the default cumulative probability test input values. */
     public abstract int[] makeCumulativeTestPoints();
 
-    /** Creates the default cumulative probability density test expected 
values. */
+    /** Creates the default cumulative probability test expected values. */
     public abstract double[] makeCumulativeTestValues();
 
     /** Creates the default cumulative probability precision test input 
values. */
@@ -158,6 +173,8 @@ abstract class DiscreteDistributionAbstractTest {
         return new double[0];
     }
 
+    //---- Default implementations of inverse test data generation methods ----
+
     /** Creates the default inverse cumulative probability test input values. 
*/
     public abstract double[] makeInverseCumulativeTestPoints();
 
@@ -212,10 +229,10 @@ abstract class DiscreteDistributionAbstractTest {
      */
     protected void verifyDensities() {
         for (int i = 0; i < densityTestPoints.length; i++) {
-            final int testPoint = densityTestPoints[i];
+            final int x = densityTestPoints[i];
             Assertions.assertEquals(densityTestValues[i],
-                distribution.probability(testPoint), getTolerance(),
-                () -> "Incorrect density value returned for " + testPoint);
+                distribution.probability(x), getTolerance(),
+                () -> "Incorrect probability value returned for " + x);
         }
     }
 
@@ -225,10 +242,10 @@ abstract class DiscreteDistributionAbstractTest {
      */
     protected void verifyLogDensities() {
         for (int i = 0; i < densityTestPoints.length; i++) {
-            final int testPoint = densityTestPoints[i];
+            final int x = densityTestPoints[i];
             Assertions.assertEquals(logDensityTestValues[i],
-                distribution.logProbability(testPoint), tolerance,
-                () -> "Incorrect log density value returned for " + testPoint);
+                distribution.logProbability(x), getTolerance(),
+                () -> "Incorrect log probability value returned for " + x);
         }
     }
 
@@ -238,10 +255,28 @@ abstract class DiscreteDistributionAbstractTest {
      */
     protected void verifyCumulativeProbabilities() {
         for (int i = 0; i < cumulativeTestPoints.length; i++) {
-            final int testPoint = cumulativeTestPoints[i];
+            final int x = cumulativeTestPoints[i];
             Assertions.assertEquals(cumulativeTestValues[i],
-                distribution.cumulativeProbability(testPoint), getTolerance(),
-                () -> "Incorrect cumulative probability value returned for " + 
testPoint);
+                distribution.cumulativeProbability(x), getTolerance(),
+                () -> "Incorrect cumulative probability value returned for " + 
x);
+        }
+        // verify probability(double, double)
+        for (int i = 0; i < cumulativeTestPoints.length; i++) {
+            for (int j = 0; j < cumulativeTestPoints.length; j++) {
+                if (cumulativeTestPoints[i] <= cumulativeTestPoints[j]) {
+                    Assertions.assertEquals(
+                        cumulativeTestValues[j] - cumulativeTestValues[i],
+                        distribution.probability(cumulativeTestPoints[i], 
cumulativeTestPoints[j]),
+                        getTolerance());
+                } else {
+                    try {
+                        distribution.probability(cumulativeTestPoints[i], 
cumulativeTestPoints[j]);
+                    } catch (final IllegalArgumentException e) {
+                        continue;
+                    }
+                    Assertions.fail("distribution.probability(double, double) 
should have thrown an exception that second argument is too large");
+                }
+            }
         }
     }
 
@@ -311,28 +346,16 @@ abstract class DiscreteDistributionAbstractTest {
 
     //------------------------ Default test cases -----------------------------
 
-    /**
-     * Verifies that probability density calculations match expected values
-     * using default test instance data.
-     */
     @Test
     void testDensities() {
         verifyDensities();
     }
 
-    /**
-     * Verifies that logarithmic probability density calculations match 
expected values
-     * using default test instance data.
-     */
     @Test
     void testLogDensities() {
         verifyLogDensities();
     }
 
-    /**
-     * Verifies that cumulative probability density calculations match 
expected values
-     * using default test instance data.
-     */
     @Test
     void testCumulativeProbabilities() {
         verifyCumulativeProbabilities();
@@ -358,78 +381,103 @@ abstract class DiscreteDistributionAbstractTest {
         verifySurvivalProbabilityPrecision();
     }
 
-    /**
-     * Verifies that inverse cumulative probability density calculations match 
expected values
-     * using default test instance data.
-     */
     @Test
     void testInverseCumulativeProbabilities() {
         verifyInverseCumulativeProbabilities();
     }
 
+    /**
+     * Verifies that probability computations are consistent.
+     */
+    @Test
+    void testConsistency() {
+        for (int i = 1; i < cumulativeTestPoints.length; i++) {
+
+            // check that cdf(x, x) = 0
+            Assertions.assertEquals(
+                0.0,
+                distribution.probability(cumulativeTestPoints[i], 
cumulativeTestPoints[i]),
+                getTolerance());
+
+            // check that P(a < X <= b) = P(X <= b) - P(X <= a)
+            final int upper = Math.max(cumulativeTestPoints[i], 
cumulativeTestPoints[i - 1]);
+            final int lower = Math.min(cumulativeTestPoints[i], 
cumulativeTestPoints[i - 1]);
+            final double diff = distribution.cumulativeProbability(upper) -
+                distribution.cumulativeProbability(lower);
+            final double direct = distribution.probability(lower, upper);
+            Assertions.assertEquals(diff, direct, getTolerance(),
+                () -> "Inconsistent probability for (" + lower + "," + upper + 
")");
+        }
+    }
+
     @Test
-    void testConsistencyAtSupportBounds() {
-        final int lower = distribution.getSupportLowerBound();
-        Assertions.assertEquals(0.0, distribution.cumulativeProbability(lower 
- 1), 0.0,
-                "Cumulative probability must be 0 below support lower bound.");
-        Assertions.assertEquals(distribution.probability(lower), 
distribution.cumulativeProbability(lower), getTolerance(),
-                "Cumulative probability of support lower bound must be equal 
to probability mass at this point.");
-        Assertions.assertEquals(1.0, distribution.survivalProbability(lower - 
1), 0.0,
-            "Survival probability must be 1.0 below support lower bound.");
-        Assertions.assertEquals(lower, 
distribution.inverseCumulativeProbability(0.0),
-                "Inverse cumulative probability of 0 must be equal to support 
lower bound.");
-
-        final int upper = distribution.getSupportUpperBound();
-        if (upper != Integer.MAX_VALUE) {
-            Assertions.assertEquals(1.0, 
distribution.cumulativeProbability(upper), 0.0,
-                    "Cumulative probability of support upper bound must be 
equal to 1.");
-            Assertions.assertEquals(0.0, 
distribution.survivalProbability(upper), 0.0,
-                    "Survival probability of support upper bound must be equal 
to 0.");
+    void testOutsideSupport() {
+        // Test various quantities when the variable is outside the support.
+        final int lo = distribution.getSupportLowerBound();
+        Assertions.assertEquals(distribution.probability(lo), 
distribution.cumulativeProbability(lo), getTolerance());
+        Assertions.assertEquals(lo, 
distribution.inverseCumulativeProbability(0.0));
+
+        if (lo != Integer.MIN_VALUE) {
+            final int below = lo - 1;
+            Assertions.assertEquals(0.0, distribution.probability(below));
+            Assertions.assertEquals(Double.NEGATIVE_INFINITY, 
distribution.logProbability(below));
+            Assertions.assertEquals(0.0, 
distribution.cumulativeProbability(below));
+            Assertions.assertEquals(1.0, 
distribution.survivalProbability(below));
+        }
+
+        final int hi = distribution.getSupportUpperBound();
+        Assertions.assertEquals(0.0, distribution.survivalProbability(hi));
+        Assertions.assertEquals(distribution.probability(hi), 
distribution.survivalProbability(hi - 1), getTolerance());
+        Assertions.assertEquals(hi, 
distribution.inverseCumulativeProbability(1.0));
+        if (hi != Integer.MAX_VALUE) {
+            final int above = hi + 1;
+            Assertions.assertEquals(0.0, distribution.probability(above));
+            Assertions.assertEquals(Double.NEGATIVE_INFINITY, 
distribution.logProbability(above));
+            Assertions.assertEquals(1.0, 
distribution.cumulativeProbability(above));
+            Assertions.assertEquals(0.0, 
distribution.survivalProbability(above));
         }
-        Assertions.assertEquals(upper, 
distribution.inverseCumulativeProbability(1.0),
-                "Inverse cumulative probability of 1 must be equal to support 
upper bound.");
     }
 
     @Test
-    void testPrecondition1() {
+    void testProbabilityWithLowerBoundAboveUpperBound() {
         Assertions.assertThrows(DistributionException.class, () -> 
distribution.probability(1, 0));
     }
+
     @Test
-    void testPrecondition2() {
+    void testInverseCumulativeProbabilityWithProbabilityBelowZero() {
         Assertions.assertThrows(DistributionException.class, () -> 
distribution.inverseCumulativeProbability(-1));
     }
+
     @Test
-    void testPrecondition3() {
+    void testInverseCumulativeProbabilityWithProbabilityAboveOne() {
         Assertions.assertThrows(DistributionException.class, () -> 
distribution.inverseCumulativeProbability(2));
     }
 
-    /**
-     * Test sampling.
-     */
     @Test
     void testSampling() {
-        final int[] densityPoints = makeDensityTestPoints();
-        final double[] densityValues = makeDensityTestValues();
-        final int sampleSize = 1000;
-        final int length = TestUtils.eliminateZeroMassPoints(densityPoints, 
densityValues);
-        final AbstractDiscreteDistribution dist = 
(AbstractDiscreteDistribution) makeDistribution();
-        final double[] expectedCounts = new double[length];
-        final long[] observedCounts = new long[length];
-        for (int i = 0; i < length; i++) {
-            expectedCounts[i] = sampleSize * densityValues[i];
-        }
         // Use fixed seed.
+        final int sampleSize = 1000;
         final DiscreteDistribution.Sampler sampler =
-            dist.createSampler(RandomSource.create(RandomSource.WELL_512_A, 
1000));
+            
getDistribution().createSampler(RandomSource.create(RandomSource.WELL_512_A, 
1000));
         final int[] sample = TestUtils.sample(sampleSize, sampler);
+
+        final int[] densityPoints = makeDensityTestPoints();
+        final double[] densityValues = makeDensityTestValues();
+        final int length = TestUtils.eliminateZeroMassPoints(densityPoints, 
densityValues);
+        final double[] expected = Arrays.copyOf(densityValues, length);
+
+        final long[] counts = new long[length];
         for (int i = 0; i < sampleSize; i++) {
+            final int x = sample[i];
             for (int j = 0; j < length; j++) {
-                if (sample[i] == densityPoints[j]) {
-                    observedCounts[j]++;
+                if (x == densityPoints[j]) {
+                    counts[j]++;
+                    break;
                 }
             }
         }
-        TestUtils.assertChiSquareAccept(densityPoints, expectedCounts, 
observedCounts, 0.001);
+
+        TestUtils.assertChiSquareAccept(densityPoints, expected, counts, 
0.001);
     }
 
     /**
@@ -442,32 +490,47 @@ abstract class DiscreteDistributionAbstractTest {
     }
 
     //------------------ Getters / Setters for test instance data -----------
+
     /**
-     * @return Returns the cumulativeTestPoints.
+     * @return Returns the distribution.
      */
-    protected int[] getCumulativeTestPoints() {
-        return cumulativeTestPoints;
+    protected DiscreteDistribution getDistribution() {
+        return distribution;
     }
 
     /**
-     * @param cumulativeTestPoints The cumulativeTestPoints to set.
+     * @param distribution The distribution to set.
      */
-    protected void setCumulativeTestPoints(int[] cumulativeTestPoints) {
-        this.cumulativeTestPoints = cumulativeTestPoints;
+    protected void setDistribution(DiscreteDistribution distribution) {
+        this.distribution = distribution;
     }
 
     /**
-     * @return Returns the cumulativeTestValues.
+     * @return Returns the tolerance.
      */
-    protected double[] getCumulativeTestValues() {
-        return cumulativeTestValues;
+    protected double getTolerance() {
+        return tolerance;
     }
 
     /**
-     * @param cumulativeTestValues The cumulativeTestValues to set.
+     * @param tolerance The tolerance to set.
      */
-    protected void setCumulativeTestValues(double[] cumulativeTestValues) {
-        this.cumulativeTestValues = cumulativeTestValues;
+    protected void setTolerance(double tolerance) {
+        this.tolerance = tolerance;
+    }
+
+    /**
+     * @return Returns the high precision tolerance.
+     */
+    protected double getHighPrecisionTolerance() {
+        return highPrecisionTolerance;
+    }
+
+    /**
+     * @param highPrecisionTolerance The high precision highPrecisionTolerance 
to set.
+     */
+    protected void setHighPrecisionTolerance(double highPrecisionTolerance) {
+        this.highPrecisionTolerance = highPrecisionTolerance;
     }
 
     /**
@@ -517,6 +580,34 @@ abstract class DiscreteDistributionAbstractTest {
     }
 
     /**
+     * @return Returns the cumulativeTestPoints.
+     */
+    protected int[] getCumulativeTestPoints() {
+        return cumulativeTestPoints;
+    }
+
+    /**
+     * @param cumulativeTestPoints The cumulativeTestPoints to set.
+     */
+    protected void setCumulativeTestPoints(int[] cumulativeTestPoints) {
+        this.cumulativeTestPoints = cumulativeTestPoints;
+    }
+
+    /**
+     * @return Returns the cumulativeTestValues.
+     */
+    protected double[] getCumulativeTestValues() {
+        return cumulativeTestValues;
+    }
+
+    /**
+     * @param cumulativeTestValues The cumulativeTestValues to set.
+     */
+    protected void setCumulativeTestValues(double[] cumulativeTestValues) {
+        this.cumulativeTestValues = cumulativeTestValues;
+    }
+
+    /**
      * @return Returns the cumulativePrecisionTestPoints.
      */
     protected int[] getCumulativePrecisionTestPoints() {
@@ -573,20 +664,6 @@ abstract class DiscreteDistributionAbstractTest {
     }
 
     /**
-     * @return Returns the distribution.
-     */
-    protected DiscreteDistribution getDistribution() {
-        return distribution;
-    }
-
-    /**
-     * @param distribution The distribution to set.
-     */
-    protected void setDistribution(DiscreteDistribution distribution) {
-        this.distribution = distribution;
-    }
-
-    /**
      * @return Returns the inverseCumulativeTestPoints.
      */
     protected double[] getInverseCumulativeTestPoints() {
@@ -615,34 +692,6 @@ abstract class DiscreteDistributionAbstractTest {
     }
 
     /**
-     * @return Returns the tolerance.
-     */
-    protected double getTolerance() {
-        return tolerance;
-    }
-
-    /**
-     * @param tolerance The tolerance to set.
-     */
-    protected void setTolerance(double tolerance) {
-        this.tolerance = tolerance;
-    }
-
-    /**
-     * @return Returns the high precision tolerance.
-     */
-    protected double getHighPrecisionTolerance() {
-        return highPrecisionTolerance;
-    }
-
-    /**
-     * @param highPrecisionTolerance The high precision highPrecisionTolerance 
to set.
-     */
-    protected void setHighPrecisionTolerance(double highPrecisionTolerance) {
-        this.highPrecisionTolerance = highPrecisionTolerance;
-    }
-
-    /**
      * The expected value for {@link 
DiscreteDistribution#isSupportConnected()}.
      * The default is {@code true}. Test class should override this when the 
distribution
      * is not support connected.
diff --git 
a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/TruncatedNormalDistributionTest.java
 
b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/TruncatedNormalDistributionTest.java
index c837a84..bee103c 100644
--- 
a/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/TruncatedNormalDistributionTest.java
+++ 
b/commons-statistics-distribution/src/test/java/org/apache/commons/statistics/distribution/TruncatedNormalDistributionTest.java
@@ -98,7 +98,7 @@ class TruncatedNormalDistributionTest extends 
ContinuousDistributionAbstractTest
         testMoments(distribution, mean, variance);
 
         testConsistency();
-        testSampler();
+        testSampling();
         testOutsideSupport();
         testDensities();
         testLogDensities();
@@ -107,9 +107,9 @@ class TruncatedNormalDistributionTest extends 
ContinuousDistributionAbstractTest
         testDensityIntegrals();
         testCumulativeProbabilities();
         testIsSupportConnected();
-        testPrecondition1();
-        testPrecondition2();
-        testPrecondition3();
+        testProbabilityWithLowerBoundAboveUpperBound();
+        testInverseCumulativeProbabilityWithProbabilityBelowZero();
+        testInverseCumulativeProbabilityWithProbabilityAboveOne();
 
         // Bound test
         Assertions.assertTrue(distribution.getSupportLowerBound() <= 
distribution.inverseCumulativeProbability(Double.MIN_VALUE));

Reply via email to