You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2023/12/23 12:19:25 UTC
(commons-statistics) 02/06: STATISTICS-81: Add integer min and max implementation
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 3b2ae69e190675c63e22e3fa618df09bd0109719
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Tue Dec 19 10:11:07 2023 +0000
STATISTICS-81: Add integer min and max implementation
Added new interfaces for int and long statistics:
IntStatistic and LongStatistic.
Base tests have been created for each type using the format from
BaseDoubleStatisticTest.
---
.../descriptive/{Max.java => IntMax.java} | 61 +-
.../descriptive/{Min.java => IntMin.java} | 61 +-
.../statistics/descriptive/IntStatistic.java | 28 +
.../descriptive/{Max.java => LongMax.java} | 74 ++-
.../descriptive/{Min.java => LongMin.java} | 70 ++-
.../statistics/descriptive/LongStatistic.java | 28 +
.../apache/commons/statistics/descriptive/Max.java | 2 +-
.../apache/commons/statistics/descriptive/Min.java | 2 +-
.../descriptive/StatisticAccumulator.java | 35 ++
.../commons/statistics/descriptive/Statistics.java | 32 +
.../descriptive/BaseDoubleStatisticTest.java | 8 +-
...tatisticTest.java => BaseIntStatisticTest.java} | 690 ++++++++-------------
...atisticTest.java => BaseLongStatisticTest.java} | 690 ++++++++-------------
.../statistics/descriptive/BaseStatisticTest.java | 331 ++++++++++
.../descriptive/BigIntegerStatisticResult.java | 48 ++
.../commons/statistics/descriptive/IntMaxTest.java | 73 +++
.../commons/statistics/descriptive/IntMinTest.java | 73 +++
.../statistics/descriptive/IntStatisticResult.java | 48 ++
.../statistics/descriptive/LongMaxTest.java | 73 +++
.../statistics/descriptive/LongMinTest.java | 73 +++
.../descriptive/LongStatisticResult.java | 48 ++
.../commons/statistics/descriptive/ResultType.java | 31 +
.../commons/statistics/descriptive/TestHelper.java | 206 ++++++
src/conf/checkstyle/checkstyle-suppressions.xml | 2 +
24 files changed, 1841 insertions(+), 946 deletions(-)
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Max.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMax.java
similarity index 65%
copy from commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Max.java
copy to commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMax.java
index 8ac52c6..77f68a6 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Max.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMax.java
@@ -16,14 +16,14 @@
*/
package org.apache.commons.statistics.descriptive;
+import java.math.BigInteger;
+
/**
- * Returns the maximum of the available values. Uses {@link Math#max Math.max} as an
+ * Returns the maximum of the available values. Uses {@link Math#max(int, int) Math.max} as an
* underlying function to compute the {@code maximum}.
*
* <ul>
- * <li>The result is {@link Double#NEGATIVE_INFINITY negative infinity} if no values are added.
- * <li>The result is {@code NaN} if any of the values is {@code NaN}.
- * <li>The value {@code -0.0} is considered strictly smaller than {@code 0.0}.
+ * <li>The result is {@link Integer#MIN_VALUE} if no values are added.
* </ul>
*
* <p>This class is designed to work with (though does not require)
@@ -31,11 +31,11 @@ package org.apache.commons.statistics.descriptive;
*
* <p><strong>This implementation is not thread safe.</strong>
* If multiple threads access an instance of this class concurrently,
- * and at least one of the threads invokes the {@link java.util.function.DoubleConsumer#accept(double) accept} or
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method, it must be synchronized externally.
+ * and at least one of the threads invokes the {@link java.util.function.IntConsumer#accept(int) accept} or
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method, it must be synchronized externally.
*
- * <p>However, it is safe to use {@link java.util.function.DoubleConsumer#accept(double) accept}
- * and {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * <p>However, it is safe to use {@link java.util.function.IntConsumer#accept(int) accept}
+ * and {@link StatisticAccumulator#combine(StatisticResult) combine}
* as {@code accumulator} and {@code combiner} functions of
* {@link java.util.stream.Collector Collector} on a parallel stream,
* because the parallel implementation of {@link java.util.stream.Stream#collect Stream.collect()}
@@ -43,44 +43,42 @@ package org.apache.commons.statistics.descriptive;
* safe and efficient parallel execution.
*
* @since 1.1
- * @see Math#max(double, double)
+ * @see Math#max(int, int)
*/
-public final class Max implements DoubleStatistic, DoubleStatisticAccumulator<Max> {
+public final class IntMax implements IntStatistic, StatisticAccumulator<IntMax> {
/** Current maximum. */
- private double maximum = Double.NEGATIVE_INFINITY;
+ private int maximum = Integer.MIN_VALUE;
/**
* Create an instance.
*/
- private Max() {
+ private IntMax() {
// No-op
}
/**
* Creates an instance.
*
- * <p>The initial result is {@link Double#NEGATIVE_INFINITY negative infinity}.
+ * <p>The initial result is {@link Integer#MIN_VALUE}.
*
* @return {@code Max} instance.
*/
- public static Max create() {
- return new Max();
+ public static IntMax create() {
+ return new IntMax();
}
/**
* Returns an instance populated using the input {@code values}.
*
- * <p>The result is {@code NaN} if any of the values is {@code NaN}.
- *
* <p>When the input is an empty array, the result is
- * {@link Double#NEGATIVE_INFINITY negative infinity}.
+ * {@link Integer#MIN_VALUE}.
*
* @param values Values.
* @return {@code Max} instance.
*/
- public static Max of(double... values) {
- return Statistics.add(new Max(), values);
+ public static IntMax of(int... values) {
+ return Statistics.add(new IntMax(), values);
}
/**
@@ -89,7 +87,7 @@ public final class Max implements DoubleStatistic, DoubleStatisticAccumulator<Ma
* @param value Value.
*/
@Override
- public void accept(double value) {
+ public void accept(int value) {
maximum = Math.max(maximum, value);
}
@@ -97,18 +95,33 @@ public final class Max implements DoubleStatistic, DoubleStatisticAccumulator<Ma
* Gets the maximum of all input values.
*
* <p>When no values have been added, the result is
- * {@link Double#NEGATIVE_INFINITY negative infinity}.
+ * {@link Integer#MIN_VALUE}.
*
* @return maximum of all values.
*/
+ @Override
+ public int getAsInt() {
+ return maximum;
+ }
+
+ @Override
+ public long getAsLong() {
+ return maximum;
+ }
+
@Override
public double getAsDouble() {
return maximum;
}
@Override
- public Max combine(Max other) {
- accept(other.getAsDouble());
+ public BigInteger getAsBigInteger() {
+ return BigInteger.valueOf(maximum);
+ }
+
+ @Override
+ public IntMax combine(IntMax other) {
+ accept(other.getAsInt());
return this;
}
}
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMin.java
similarity index 65%
copy from commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java
copy to commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMin.java
index a7f923f..83ca12a 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMin.java
@@ -16,14 +16,14 @@
*/
package org.apache.commons.statistics.descriptive;
+import java.math.BigInteger;
+
/**
- * Returns the minimum of the available values. Uses {@link Math#min Math.min} as an
+ * Returns the minimum of the available values. Uses {@link Math#min(int, int) Math.min} as an
* underlying function to compute the {@code minimum}.
*
* <ul>
- * <li>The result is {@link Double#POSITIVE_INFINITY positive infinity} if no values are added.
- * <li>The result is {@code NaN} if any of the values is {@code NaN}.
- * <li>The value {@code -0.0} is considered strictly smaller than {@code 0.0}.
+ * <li>The result is {@link Integer#MAX_VALUE} if no values are added.
* </ul>
*
* <p>This class is designed to work with (though does not require)
@@ -31,11 +31,11 @@ package org.apache.commons.statistics.descriptive;
*
* <p><strong>This implementation is not thread safe.</strong>
* If multiple threads access an instance of this class concurrently,
- * and at least one of the threads invokes the {@link java.util.function.DoubleConsumer#accept(double) accept} or
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method, it must be synchronized externally.
+ * and at least one of the threads invokes the {@link java.util.function.IntConsumer#accept(int) accept} or
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method, it must be synchronized externally.
*
- * <p>However, it is safe to use {@link java.util.function.DoubleConsumer#accept(double) accept}
- * and {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * <p>However, it is safe to use {@link java.util.function.IntConsumer#accept(int) accept}
+ * and {@link StatisticAccumulator#combine(StatisticResult) combine}
* as {@code accumulator} and {@code combiner} functions of
* {@link java.util.stream.Collector Collector} on a parallel stream,
* because the parallel implementation of {@link java.util.stream.Stream#collect Stream.collect()}
@@ -43,44 +43,42 @@ package org.apache.commons.statistics.descriptive;
* safe and efficient parallel execution.
*
* @since 1.1
- * @see Math#min(double, double)
+ * @see Math#min(int, int)
*/
-public final class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+public final class IntMin implements IntStatistic, StatisticAccumulator<IntMin> {
/** Current minimum. */
- private double minimum = Double.POSITIVE_INFINITY;
+ private int minimum = Integer.MAX_VALUE;
/**
* Create an instance.
*/
- private Min() {
+ private IntMin() {
// No-op
}
/**
* Creates an instance.
*
- * <p>The initial result is {@link Double#POSITIVE_INFINITY positive infinity}.
+ * <p>The initial result is {@link Integer#MAX_VALUE}.
*
* @return {@code Min} instance.
*/
- public static Min create() {
- return new Min();
+ public static IntMin create() {
+ return new IntMin();
}
/**
* Returns an instance populated using the input {@code values}.
*
- * <p>The result is {@code NaN} if any of the values is {@code NaN}.
- *
* <p>When the input is an empty array, the result is
- * {@link Double#POSITIVE_INFINITY positive infinity}.
+ * {@link Integer#MAX_VALUE}.
*
* @param values Values.
* @return {@code Min} instance.
*/
- public static Min of(double... values) {
- return Statistics.add(new Min(), values);
+ public static IntMin of(int... values) {
+ return Statistics.add(new IntMin(), values);
}
/**
@@ -89,7 +87,7 @@ public final class Min implements DoubleStatistic, DoubleStatisticAccumulator<Mi
* @param value Value.
*/
@Override
- public void accept(double value) {
+ public void accept(int value) {
minimum = Math.min(minimum, value);
}
@@ -97,18 +95,33 @@ public final class Min implements DoubleStatistic, DoubleStatisticAccumulator<Mi
* Gets the minimum of all input values.
*
* <p>When no values have been added, the result is
- * {@link Double#POSITIVE_INFINITY positive infinity}.
+ * {@link Integer#MAX_VALUE}.
*
* @return minimum of all values.
*/
+ @Override
+ public int getAsInt() {
+ return minimum;
+ }
+
+ @Override
+ public long getAsLong() {
+ return minimum;
+ }
+
@Override
public double getAsDouble() {
return minimum;
}
@Override
- public Min combine(Min other) {
- accept(other.getAsDouble());
+ public BigInteger getAsBigInteger() {
+ return BigInteger.valueOf(minimum);
+ }
+
+ @Override
+ public IntMin combine(IntMin other) {
+ accept(other.getAsInt());
return this;
}
}
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntStatistic.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntStatistic.java
new file mode 100644
index 0000000..24d53f7
--- /dev/null
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntStatistic.java
@@ -0,0 +1,28 @@
+/*
+ * 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.statistics.descriptive;
+
+import java.util.function.IntConsumer;
+
+/**
+ * Represents a state object for computing a statistic over {@code int} valued input(s).
+ *
+ * @since 1.1
+ */
+public interface IntStatistic extends IntConsumer, StatisticResult {
+ // Composite interface
+}
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Max.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongMax.java
similarity index 60%
copy from commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Max.java
copy to commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongMax.java
index 8ac52c6..5eab331 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Max.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongMax.java
@@ -16,14 +16,14 @@
*/
package org.apache.commons.statistics.descriptive;
+import java.math.BigInteger;
+
/**
- * Returns the maximum of the available values. Uses {@link Math#max Math.max} as an
+ * Returns the maximum of the available values. Uses {@link Math#max(long, long) Math.max} as an
* underlying function to compute the {@code maximum}.
*
* <ul>
- * <li>The result is {@link Double#NEGATIVE_INFINITY negative infinity} if no values are added.
- * <li>The result is {@code NaN} if any of the values is {@code NaN}.
- * <li>The value {@code -0.0} is considered strictly smaller than {@code 0.0}.
+ * <li>The result is {@link Long#MIN_VALUE} if no values are added.
* </ul>
*
* <p>This class is designed to work with (though does not require)
@@ -31,11 +31,11 @@ package org.apache.commons.statistics.descriptive;
*
* <p><strong>This implementation is not thread safe.</strong>
* If multiple threads access an instance of this class concurrently,
- * and at least one of the threads invokes the {@link java.util.function.DoubleConsumer#accept(double) accept} or
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method, it must be synchronized externally.
+ * and at least one of the threads invokes the {@link java.util.function.LongConsumer#accept(long) accept} or
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method, it must be synchronized externally.
*
- * <p>However, it is safe to use {@link java.util.function.DoubleConsumer#accept(double) accept}
- * and {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * <p>However, it is safe to use {@link java.util.function.LongConsumer#accept(long) accept}
+ * and {@link StatisticAccumulator#combine(StatisticResult) combine}
* as {@code accumulator} and {@code combiner} functions of
* {@link java.util.stream.Collector Collector} on a parallel stream,
* because the parallel implementation of {@link java.util.stream.Stream#collect Stream.collect()}
@@ -43,44 +43,42 @@ package org.apache.commons.statistics.descriptive;
* safe and efficient parallel execution.
*
* @since 1.1
- * @see Math#max(double, double)
+ * @see Math#max(long, long)
*/
-public final class Max implements DoubleStatistic, DoubleStatisticAccumulator<Max> {
+public final class LongMax implements LongStatistic, StatisticAccumulator<LongMax> {
/** Current maximum. */
- private double maximum = Double.NEGATIVE_INFINITY;
+ private long maximum = Long.MIN_VALUE;
/**
* Create an instance.
*/
- private Max() {
+ private LongMax() {
// No-op
}
/**
* Creates an instance.
*
- * <p>The initial result is {@link Double#NEGATIVE_INFINITY negative infinity}.
+ * <p>The initial result is {@link Long#MIN_VALUE}.
*
- * @return {@code Max} instance.
+ * @return {@code Min} instance.
*/
- public static Max create() {
- return new Max();
+ public static LongMax create() {
+ return new LongMax();
}
/**
* Returns an instance populated using the input {@code values}.
*
- * <p>The result is {@code NaN} if any of the values is {@code NaN}.
- *
* <p>When the input is an empty array, the result is
- * {@link Double#NEGATIVE_INFINITY negative infinity}.
+ * {@link Long#MIN_VALUE}.
*
* @param values Values.
- * @return {@code Max} instance.
+ * @return {@code Min} instance.
*/
- public static Max of(double... values) {
- return Statistics.add(new Max(), values);
+ public static LongMax of(long... values) {
+ return Statistics.add(new LongMax(), values);
}
/**
@@ -89,7 +87,7 @@ public final class Max implements DoubleStatistic, DoubleStatisticAccumulator<Ma
* @param value Value.
*/
@Override
- public void accept(double value) {
+ public void accept(long value) {
maximum = Math.max(maximum, value);
}
@@ -97,18 +95,42 @@ public final class Max implements DoubleStatistic, DoubleStatisticAccumulator<Ma
* Gets the maximum of all input values.
*
* <p>When no values have been added, the result is
- * {@link Double#NEGATIVE_INFINITY negative infinity}.
+ * {@link Long#MIN_VALUE}.
+ *
+ * @return maximum of all values.
+ */
+ @Override
+ public long getAsLong() {
+ return maximum;
+ }
+
+ /**
+ * Gets the maximum of all input values.
+ *
+ * <p>This method will throw an {@link ArithmeticException} if the {@code long}
+ * maximum overflows an {@code int}; or no values have been added.
*
* @return maximum of all values.
+ * @see Math#toIntExact(long)
*/
+ @Override
+ public int getAsInt() {
+ return Math.toIntExact(maximum);
+ }
+
@Override
public double getAsDouble() {
return maximum;
}
@Override
- public Max combine(Max other) {
- accept(other.getAsDouble());
+ public BigInteger getAsBigInteger() {
+ return BigInteger.valueOf(maximum);
+ }
+
+ @Override
+ public LongMax combine(LongMax other) {
+ accept(other.getAsLong());
return this;
}
}
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongMin.java
similarity index 61%
copy from commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java
copy to commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongMin.java
index a7f923f..cb9df20 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongMin.java
@@ -16,14 +16,14 @@
*/
package org.apache.commons.statistics.descriptive;
+import java.math.BigInteger;
+
/**
- * Returns the minimum of the available values. Uses {@link Math#min Math.min} as an
+ * Returns the minimum of the available values. Uses {@link Math#min(long, long) Math.min} as an
* underlying function to compute the {@code minimum}.
*
* <ul>
- * <li>The result is {@link Double#POSITIVE_INFINITY positive infinity} if no values are added.
- * <li>The result is {@code NaN} if any of the values is {@code NaN}.
- * <li>The value {@code -0.0} is considered strictly smaller than {@code 0.0}.
+ * <li>The result is {@link Long#MAX_VALUE} if no values are added.
* </ul>
*
* <p>This class is designed to work with (though does not require)
@@ -31,11 +31,11 @@ package org.apache.commons.statistics.descriptive;
*
* <p><strong>This implementation is not thread safe.</strong>
* If multiple threads access an instance of this class concurrently,
- * and at least one of the threads invokes the {@link java.util.function.DoubleConsumer#accept(double) accept} or
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method, it must be synchronized externally.
+ * and at least one of the threads invokes the {@link java.util.function.LongConsumer#accept(long) accept} or
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method, it must be synchronized externally.
*
- * <p>However, it is safe to use {@link java.util.function.DoubleConsumer#accept(double) accept}
- * and {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * <p>However, it is safe to use {@link java.util.function.LongConsumer#accept(long) accept}
+ * and {@link StatisticAccumulator#combine(StatisticResult) combine}
* as {@code accumulator} and {@code combiner} functions of
* {@link java.util.stream.Collector Collector} on a parallel stream,
* because the parallel implementation of {@link java.util.stream.Stream#collect Stream.collect()}
@@ -43,44 +43,42 @@ package org.apache.commons.statistics.descriptive;
* safe and efficient parallel execution.
*
* @since 1.1
- * @see Math#min(double, double)
+ * @see Math#min(long, long)
*/
-public final class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+public final class LongMin implements LongStatistic, StatisticAccumulator<LongMin> {
/** Current minimum. */
- private double minimum = Double.POSITIVE_INFINITY;
+ private long minimum = Long.MAX_VALUE;
/**
* Create an instance.
*/
- private Min() {
+ private LongMin() {
// No-op
}
/**
* Creates an instance.
*
- * <p>The initial result is {@link Double#POSITIVE_INFINITY positive infinity}.
+ * <p>The initial result is {@link Long#MAX_VALUE}.
*
* @return {@code Min} instance.
*/
- public static Min create() {
- return new Min();
+ public static LongMin create() {
+ return new LongMin();
}
/**
* Returns an instance populated using the input {@code values}.
*
- * <p>The result is {@code NaN} if any of the values is {@code NaN}.
- *
* <p>When the input is an empty array, the result is
- * {@link Double#POSITIVE_INFINITY positive infinity}.
+ * {@link Long#MAX_VALUE}.
*
* @param values Values.
* @return {@code Min} instance.
*/
- public static Min of(double... values) {
- return Statistics.add(new Min(), values);
+ public static LongMin of(long... values) {
+ return Statistics.add(new LongMin(), values);
}
/**
@@ -89,7 +87,7 @@ public final class Min implements DoubleStatistic, DoubleStatisticAccumulator<Mi
* @param value Value.
*/
@Override
- public void accept(double value) {
+ public void accept(long value) {
minimum = Math.min(minimum, value);
}
@@ -97,18 +95,42 @@ public final class Min implements DoubleStatistic, DoubleStatisticAccumulator<Mi
* Gets the minimum of all input values.
*
* <p>When no values have been added, the result is
- * {@link Double#POSITIVE_INFINITY positive infinity}.
+ * {@link Long#MAX_VALUE}.
+ *
+ * @return minimum of all values.
+ */
+ @Override
+ public long getAsLong() {
+ return minimum;
+ }
+
+ /**
+ * Gets the minimum of all input values.
+ *
+ * <p>This method will throw an {@link ArithmeticException} if the {@code long}
+ * minimum overflows an {@code int}; or no values have been added.
*
* @return minimum of all values.
+ * @see Math#toIntExact(long)
*/
+ @Override
+ public int getAsInt() {
+ return Math.toIntExact(minimum);
+ }
+
@Override
public double getAsDouble() {
return minimum;
}
@Override
- public Min combine(Min other) {
- accept(other.getAsDouble());
+ public BigInteger getAsBigInteger() {
+ return BigInteger.valueOf(minimum);
+ }
+
+ @Override
+ public LongMin combine(LongMin other) {
+ accept(other.getAsLong());
return this;
}
}
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongStatistic.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongStatistic.java
new file mode 100644
index 0000000..95d0f06
--- /dev/null
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongStatistic.java
@@ -0,0 +1,28 @@
+/*
+ * 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.statistics.descriptive;
+
+import java.util.function.LongConsumer;
+
+/**
+ * Represents a state object for computing a statistic over {@code long} valued input(s).
+ *
+ * @since 1.1
+ */
+public interface LongStatistic extends LongConsumer, StatisticResult {
+ // Composite interface
+}
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Max.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Max.java
index 8ac52c6..16709da 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Max.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Max.java
@@ -17,7 +17,7 @@
package org.apache.commons.statistics.descriptive;
/**
- * Returns the maximum of the available values. Uses {@link Math#max Math.max} as an
+ * Returns the maximum of the available values. Uses {@link Math#max(double, double) Math.max} as an
* underlying function to compute the {@code maximum}.
*
* <ul>
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java
index a7f923f..19f301f 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java
@@ -17,7 +17,7 @@
package org.apache.commons.statistics.descriptive;
/**
- * Returns the minimum of the available values. Uses {@link Math#min Math.min} as an
+ * Returns the minimum of the available values. Uses {@link Math#min(double, double) Math.min} as an
* underlying function to compute the {@code minimum}.
*
* <ul>
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/StatisticAccumulator.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/StatisticAccumulator.java
new file mode 100644
index 0000000..693396d
--- /dev/null
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/StatisticAccumulator.java
@@ -0,0 +1,35 @@
+/*
+ * 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.statistics.descriptive;
+
+/**
+ * A mutable result container that accumulates a {@link StatisticResult}.
+ *
+ * @param <T> {@link StatisticResult} being accumulated.
+ *
+ * @since 1.1
+ */
+public interface StatisticAccumulator<T extends StatisticResult> {
+
+ /**
+ * Combines the state of the {@code other} statistic into this one.
+ *
+ * @param other Another statistic to be combined.
+ * @return {@code this} instance after combining {@code other}.
+ */
+ T combine(T other);
+}
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Statistics.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Statistics.java
index 34d5df8..73c8a40 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Statistics.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Statistics.java
@@ -17,6 +17,8 @@
package org.apache.commons.statistics.descriptive;
import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
/**
* Utility methods for statistics.
@@ -43,6 +45,36 @@ final class Statistics {
return statistic;
}
+ /**
+ * Add all the {@code values} to the {@code statistic}.
+ *
+ * @param <T> Type of the statistic
+ * @param statistic Statistic.
+ * @param values Values.
+ * @return the statistic
+ */
+ static <T extends IntConsumer> T add(T statistic, int[] values) {
+ for (final int x : values) {
+ statistic.accept(x);
+ }
+ return statistic;
+ }
+
+ /**
+ * Add all the {@code values} to the {@code statistic}.
+ *
+ * @param <T> Type of the statistic
+ * @param statistic Statistic.
+ * @param values Values.
+ * @return the statistic
+ */
+ static <T extends LongConsumer> T add(T statistic, long[] values) {
+ for (final long x : values) {
+ statistic.accept(x);
+ }
+ return statistic;
+ }
+
/**
* Returns {@code true} if the second central moment {@code m2} is effectively
* zero given the magnitude of the first raw moment {@code m1}.
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseDoubleStatisticTest.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseDoubleStatisticTest.java
index 6e32115..bdda305 100644
--- a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseDoubleStatisticTest.java
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseDoubleStatisticTest.java
@@ -57,7 +57,7 @@ import org.junit.jupiter.params.provider.MethodSource;
* {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
* </ol>
*
- * <p>An test implementation has to provide these method implementations:
+ * <p>A test implementation has to provide these method implementations:
*
* <ul>
* <li>{@link #create()}: Create an empty statistic.
@@ -208,8 +208,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
private final Double expected;
/**
- * @param expected Expected statistic value.
* @param values Sample values.
+ * @param expected Expected statistic value.
*/
TestData(double[] values, double expected) {
this.values = values;
@@ -425,7 +425,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* Map the {@code value} to the valid domain of the statistic. This method is called
* with the example data before {@link #getExpectedValue(double[])} and
* {@link #getExpectedNonFiniteValue(double[])}. In the later case the method is only
- * called with finite value. It can be used by implementing classes to adjust the data
+ * called with finite values. It can be used by implementing classes to adjust the data
* to a valid domain, for example if computing the sum-of-logs all values can be
* updated to be strictly positive.
*
@@ -1373,7 +1373,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param seed Seed.
* @param repeat Repeat of the total random permutations.
*/
- private void rethrowWithSeedAndRepeat(AssertionError e, long[] seed, int repeat) {
+ private static void rethrowWithSeedAndRepeat(AssertionError e, long[] seed, int repeat) {
throw new AssertionError(String.format("%s; Seed=%s; Repeat=%d/%d",
e.getMessage(), Arrays.toString(seed), repeat, RANDOM_PERMUTATIONS), e);
}
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseDoubleStatisticTest.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseIntStatisticTest.java
similarity index 60%
copy from commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseDoubleStatisticTest.java
copy to commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseIntStatisticTest.java
index 6e32115..7b05937 100644
--- a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseDoubleStatisticTest.java
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseIntStatisticTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.commons.statistics.descriptive;
+import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -26,7 +27,6 @@ import java.util.stream.Stream;
import java.util.stream.Stream.Builder;
import org.apache.commons.rng.UniformRandomProvider;
import org.apache.commons.statistics.distribution.DoubleTolerance;
-import org.apache.commons.statistics.distribution.DoubleTolerances;
import org.apache.commons.statistics.distribution.TestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@@ -39,36 +39,43 @@ import org.junit.jupiter.params.provider.MethodSource;
/**
* Abstract base class for statistic tests.
- * This supports testing statistics that implement {@link DoubleStatistic} and
- * {@link DoubleStatisticAccumulator}.
+ * This supports testing statistics that implement {@link IntStatistic} and
+ * {@link StatisticAccumulator}.
*
* <p>This class uses parameterized tests that are repeated for instances of a
- * statistic. The statistic is tested using standard finite and non-finite data.
+ * statistic. The statistic is tested using standard data.
* Additional data may be provided by implementations.
*
* <p>There are 4 main tests for a statistic populated with values using:
*
* <ol>
- * <li>{@link java.util.function.DoubleConsumer#accept accept}
- * <li>{@link java.util.function.DoubleConsumer#accept accept} and
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
- * <li>{@link #create(double...)}
- * <li>{@link #create(double...)} and
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * <li>{@link java.util.function.IntConsumer#accept accept}
+ * <li>{@link java.util.function.IntConsumer#accept accept} and
+ * {@link StatisticAccumulator#combine(StatisticResult) combine}
+ * <li>{@link #create(int...)}
+ * <li>{@link #create(int...)} and
+ * {@link StatisticAccumulator#combine(StatisticResult) combine}
* </ol>
*
- * <p>An test implementation has to provide these method implementations:
+ * <p>Note: Integer statistics may return the expected result as a {@code double}, {@code int},
+ * {@code long}, or {@code BigInteger}. The default is {@code double}.
+ *
+ * <p>A test implementation has to provide these method implementations:
*
* <ul>
+ * <li>{@link #getResultType()}: Get the native result type of the statistic.
+ * This is used to verify that test cases are added using the correct type and
+ * test tolerances are configured for {@code double} result types.
* <li>{@link #create()}: Create an empty statistic.
- * <li>{@link #create(double...)}: Create a statistic from a set of values.
+ * <li>{@link #create(int...)}: Create a statistic from a set of values.
* <li>{@link #getEmptyValue()}: The expected value of a statistic when not enough values have
* been observed. The minimum number of values can be provided in {@link #getEmptySize()}.
- * <li>{@link #getExpectedValue(double[])}: A method to compute an expected value for the
+ * <li>{@link #getExpectedValue(int[])}: A method to compute an expected value for the
* statistic. This is used to create the expected value on standard test data. It should
* be an implementation of the canonical definition of the statistic. It can use extended
* precision to provided an accurate expected result.
- * <li>{@link #getTolerance()}: The test tolerance for all tests. This method is not used
+ * <li>{@link #getTolerance()}: The test tolerance for all tests when the expected value is
+ * a {@code double}. Tolerances are not used for integer results. This method is not used
* directly by the base class for test cases. It provides the default implementation for
* the 4 methods to provide the tolerance for each of the 4 main test cases.
* <em>Note that the default implementation throws an exception</em>. This forces the
@@ -77,12 +84,6 @@ import org.junit.jupiter.params.provider.MethodSource;
* precision for creation of the statistic from a stream of values or an array then
* it can override {@link #getToleranceAccept()}, {@link #getToleranceArray()} and
* their combine equivalents.
- * <li>{@link #getExpectedNonFiniteValue(double[])}: A method to compute an expected value
- * for the statistic when a non-finite value is encountered. This is only used with at
- * least one non-finite value. Typically this should return {@code NaN} or an infinity.
- * Data tested with non-finite values will use {@link #getToleranceNonFinite()} which
- * defaults to an exact binary equality. This may be overridden for example to return
- * a tolerance that matches any non-finite value.
* <li>{@link #streamTestData()}: Provide custom data for the test. It is
* <em>strongly recommended</em> to provide reference data from independent implementations.
* Edge case data for the statistic can also be provided. See the method javadoc for
@@ -96,7 +97,7 @@ import org.junit.jupiter.params.provider.MethodSource;
* The default uses the class name without the "Test" suffix.
* <li>{@link #isCombineSymmetric()}: Specify whether to test {@code a.combine(b)} is exactly
* equal to {@code b.combine(a)}. The default is {@code true}.
- * <li>{@link #mapValue(double)}: A method to update the sample data to the valid domain
+ * <li>{@link #mapValue(int)}: A method to update the sample data to the valid domain
* for the statistic. This can be used to alter the default test data, for example
* by mapping any negative values to positive.
* </ul>
@@ -107,66 +108,36 @@ import org.junit.jupiter.params.provider.MethodSource;
* @param <S> Statistic type
*/
@TestInstance(Lifecycle.PER_CLASS)
-abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatisticAccumulator<S>> {
- /** An empty {@code double[]}. */
- private static final double[] EMPTY = {};
- /** The number of random permutations to perform. */
- private static final int RANDOM_PERMUTATIONS = 5;
- /** The maximum number of values to output in assertion messages. This is used to provide
- * the data for the failed test which is useful to identify the failed test case. */
- private static final int MAX_FORMAT_VALUES = 100;
+abstract class BaseIntStatisticTest<S extends IntStatistic & StatisticAccumulator<S>>
+ extends BaseStatisticTest {
+ /** An empty {@code int[]}. */
+ private static final int[] EMPTY = {};
// Standard test data samples using SciPy version 1.11:
// import scipy.stats
/** Uniform samples: scipy.stats.randint.rvs(-15, 25, size=100).
* https://en.wikipedia.org/wiki/Discrete_uniform_distribution */
- private static final double[] V1 = {20, 8, 1, 2, 24, -13, -1, 19, 0, 23, 14, 2, -9, 0, 7, -15, -2, -9, -1, 12, -12,
+ private static final int[] V1 = {20, 8, 1, 2, 24, -13, -1, 19, 0, 23, 14, 2, -9, 0, 7, -15, -2, -9, -1, 12, -12,
9, 2, 12, 0, 5, 18, 15, -10, 6, 14, 6, 24, 12, -2, 1, -9, 17, 10, 7, 4, -8, 7, 7, -9, 13, -13, 0, -13, 6, 10,
-6, 23, 2, 6, -10, 12, 6, -6, 4, -5, -9, 5, 16, -14, -4, 22, -4, 20, 12, -13, 6, -13, 12, -3, 13, 1, -6, 13,
-14, -8, 4, 24, 10, 22, 8, -3, -5, 6, 9, 2, 16, 20, -8, -5, 23, 19, 20, -10, 21};
/** Poisson samples: scipy.stats.poisson.rvs(1.5, size=100).
* https://en.wikipedia.org/wiki/Poisson_distribution */
- private static final double[] V2 = {3, 0, 3, 4, 0, 1, 1, 3, 2, 1, 3, 1, 5, 0, 0, 0, 3, 3, 2, 0, 3, 1, 2, 1, 1, 0, 0,
+ private static final int[] V2 = {3, 0, 3, 4, 0, 1, 1, 3, 2, 1, 3, 1, 5, 0, 0, 0, 3, 3, 2, 0, 3, 1, 2, 1, 1, 0, 0,
1, 0, 1, 3, 0, 0, 2, 2, 0, 2, 1, 2, 1, 3, 2, 1, 2, 0, 2, 2, 1, 2, 0, 3, 2, 0, 1, 2, 2, 1, 2, 1, 0, 2, 0, 2, 2,
3, 2, 1, 1, 0, 1, 2, 1, 0, 0, 6, 2, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 2, 1, 2, 4, 2, 4, 2, 0, 0, 1, 1, 2, 2, 3};
/** Poisson samples: scipy.stats.poisson.rvs(4.5, size=100). */
- private static final double[] V3 = {1, 5, 5, 2, 1, 7, 5, 5, 3, 5, 5, 2, 4, 4, 6, 2, 7, 5, 3, 6, 7, 3, 5, 3, 7, 6, 4,
+ private static final int[] V3 = {1, 5, 5, 2, 1, 7, 5, 5, 3, 5, 5, 2, 4, 4, 6, 2, 7, 5, 3, 6, 7, 3, 5, 3, 7, 6, 4,
3, 3, 3, 3, 3, 5, 3, 2, 8, 2, 4, 3, 1, 2, 5, 4, 3, 5, 4, 4, 8, 6, 2, 4, 3, 5, 5, 6, 3, 1, 6, 8, 6, 3, 6, 10, 4,
5, 2, 4, 2, 2, 8, 2, 2, 4, 1, 4, 2, 5, 3, 2, 4, 4, 6, 9, 4, 5, 9, 9, 6, 3, 5, 3, 3, 5, 7, 5, 2, 3, 7, 4, 5};
/** Poisson samples: scipy.stats.poisson.rvs(45, size=100). */
- private static final double[] V4 = {42, 51, 38, 38, 49, 48, 42, 47, 51, 46, 45, 35, 39, 42, 49, 55, 53, 46, 49, 56,
+ private static final int[] V4 = {42, 51, 38, 38, 49, 48, 42, 47, 51, 46, 45, 35, 39, 42, 49, 55, 53, 46, 49, 56,
42, 46, 42, 53, 43, 55, 49, 52, 51, 45, 40, 49, 39, 40, 46, 43, 46, 48, 36, 44, 40, 49, 49, 43, 45, 44, 41, 55,
52, 45, 57, 41, 43, 44, 38, 52, 44, 45, 43, 42, 38, 37, 47, 42, 47, 45, 70, 45, 50, 47, 46, 50, 47, 35, 43, 52,
51, 41, 45, 42, 45, 53, 46, 48, 51, 43, 63, 48, 49, 41, 58, 51, 59, 43, 39, 32, 35, 46, 50, 50};
- /** Normal samples: scipy.stats.norm.rvs(loc=3.4, scale=2.25, size=100). */
- private static final double[] V5 = {1.06356579, -1.52552007, 7.09739891, -0.41516549, 0.17131653, 0.77923148,
- 2.90491862, 4.12648256, 5.04920689, 4.20053484, 5.83485097, 4.33138009, 4.18795702, 3.269289, 2.2399589,
- 4.16551591, -1.67192439, 1.44919254, 3.52270229, -1.49186865, -0.30794835, 5.82394621, 4.84755567, 4.79622486,
- 5.12461983, 2.62561931, 5.12457788, 8.24460895, 4.91249002, 3.75550863, 4.35440479, 4.17587334, -0.34934393,
- 2.98071452, -1.35620308, 1.93956508, 7.57171999, 5.41976186, 2.8427556, 3.04101193, 2.20374721, 4.65406057,
- 5.76961878, 3.14412957, 7.60322297, 1.598286, 2.51552974, 0.67767289, 0.76514432, 3.65663671, 0.53116457,
- 2.79439061, 7.58564809, 4.16735822, 2.95210392, 6.37867376, 6.57010411, 0.11837698, 9.16270054, 3.80097588,
- 5.48811672, 3.83378268, 2.03669252, 5.34865676, 3.11338528, 4.70088345, 6.00069684, 0.16144587, 4.22654482,
- 2.2722623, 5.39142224, 0.811471, 2.74523433, 6.32457234, 0.73033045, 9.54402353, 0.4800466, 2.00806359,
- 6.06115109, 2.3072464, 5.40974674, 2.05533169, 0.97160161, 8.06915145, 4.40792026, 4.53139251, 3.32350119,
- 1.53645238, 3.49059212, 3.57904997, 0.58634639, 5.87567911, 3.49424866, 5.72228178, 4.41403447, 1.27815121,
- 7.13861948, 4.68209093, 6.4598438, 0.66270586};
- /** Gamma samples: scipy.stats.gamma.rvs(9, scale=0.5, size=100). */
- private static final double[] V6 = {3.46479451, 8.80950045, 3.91437318, 4.23327834, 2.6910161, 4.51122052,
- 5.81939474, 3.9142699, 7.75537607, 6.06693317, 3.29388792, 3.90689471, 3.26357137, 3.6398822, 5.60048428,
- 3.68248997, 5.09297897, 4.6302593, 7.01654777, 4.2244833, 2.75326355, 5.36988549, 2.88392811, 3.50131464,
- 4.81183009, 4.92155284, 4.37061644, 3.8064197, 3.31941113, 5.01257676, 3.48037207, 2.62777255, 6.2447332,
- 6.18425783, 3.06915179, 6.42851381, 3.8969583, 3.48723372, 3.49516941, 2.90404439, 2.25920041, 3.68515649,
- 5.09607663, 3.18984299, 2.49261713, 3.9345895, 6.01480539, 8.8065787, 4.3464082, 5.03522483, 4.05315513,
- 6.07365399, 4.34804323, 5.16061656, 3.24706079, 2.89888437, 6.24575902, 3.10893227, 3.74196045, 3.94099137,
- 3.33951846, 4.9264514, 5.21935748, 5.06904776, 2.77543623, 3.72451685, 6.35546017, 4.42425655, 5.99568005,
- 4.2602446, 3.75834066, 5.17730802, 3.60682583, 4.09703419, 5.59942582, 3.49191032, 3.02164323, 8.85183547,
- 5.58631958, 3.24891648, 4.03267796, 4.30984912, 3.78187375, 5.98969913, 1.68855026, 8.43117397, 3.808258,
- 4.82043745, 2.91020117, 4.12921107, 3.54350667, 4.60545934, 5.63180941, 5.07331453, 5.04419517, 3.78796082,
- 4.25143811, 6.3242129, 4.25630677, 4.59158821};
-
- /** Sizes to cut the sample data into chunks. The {@link #cut(double[], int...)} method is robust
+
+ /** Sizes to cut the sample data into chunks. The {@link #cut(int[], int...)} method is robust
* to the sizes summing to more than the sample length. If they sum below then the expected
* value may be incorrect as all data is not used. These all sum to 100 which imposes a maximum
* length on sample data. */
@@ -180,40 +151,43 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
};
- /** Sizes to cut the non-finite sample data into chunks.
+ /** Sizes to cut the extreme value sample data into chunks.
* These all sum to 10 which imposes a maximum length on sample data. */
- private static final int[][] SIZES_NON_FINITE = {
+ private static final int[][] SIZES_EXTREME = {
{5, 5},
{3, 3, 4},
{2, 2, 2, 2, 2},
};
+ /** The statistic name. */
+ private String statisticName;
+
/** The test data and expected values. */
private final List<TestData> data = new ArrayList<>();
- /** The non-finite test data and expected values. */
- private final List<TestData> dataNonFinite = new ArrayList<>();
+ /** The extreme value test data and expected values. */
+ private final List<TestData> dataExtremeValue = new ArrayList<>();
/** Cache of the custom test data. */
private final List<StatisticTestData> dataCustom = new ArrayList<>();
- /** The statistic name. */
- private String statisticName;
-
/**
* Container for test data.
+ *
+ * <p>The statistics is expected to be either a {@code double}, {@code int}, {@code long}
+ * of {@code BigInteger} value.
*/
private static class TestData {
/** The sample values. */
- private final double[] values;
+ private final int[] values;
/** The expected statistic value. */
- private final Double expected;
+ private final StatisticResult expected;
/**
- * @param expected Expected statistic value.
* @param values Sample values.
+ * @param expected Expected statistic value.
*/
- TestData(double[] values, double expected) {
+ TestData(int[] values, StatisticResult expected) {
this.values = values;
- this.expected = Double.valueOf(expected);
+ this.expected = expected;
}
/**
@@ -221,7 +195,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*
* @return the values
*/
- double[] getValues() {
+ int[] getValues() {
return values.clone();
}
@@ -230,13 +204,14 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*
* @return the expected
*/
- Double getExpected() {
+ StatisticResult getExpected() {
return expected;
}
}
/**
- * Container for test data with tolerances for the tests.
+ * Container for test data with tolerances for the tests. Tolerances are used only
+ * for expected values as a {@code double}.
*/
static class StatisticTestData extends TestData {
/** The tolerance for the accept test. */
@@ -248,12 +223,24 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/** The tolerance for the array+combine test. */
private final DoubleTolerance tolArrayCombine;
+ /**
+ * @param values Sample values.
+ * @param expected Expected statistic value.
+ */
+ StatisticTestData(int[] values, StatisticResult expected) {
+ super(values, expected);
+ this.tolAccept = null;
+ this.tolArray = null;
+ this.tolAcceptCombine = null;
+ this.tolArrayCombine = null;
+ }
+
/**
* @param values Sample values.
* @param expected Expected statistic value.
* @param tol Tolerance for the tests.
*/
- StatisticTestData(double[] values, double expected, DoubleTolerance tol) {
+ StatisticTestData(int[] values, StatisticResult expected, DoubleTolerance tol) {
this(values, expected, tol, tol, tol, tol);
}
@@ -265,7 +252,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param tolAcceptCombine Tolerance for the test of the statistic using the accept and combine methods.
* @param tolArrayCombine Tolerance for the test of the statistic using the array create and combine methods.
*/
- StatisticTestData(double[] values, double expected,
+ StatisticTestData(int[] values, StatisticResult expected,
DoubleTolerance tolAccept, DoubleTolerance tolArray,
DoubleTolerance tolAcceptCombine, DoubleTolerance tolArrayCombine) {
super(values, expected);
@@ -277,7 +264,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Gets the tolerance for the
- * {@link BaseDoubleStatisticTest#testAccept(double[], double, DoubleTolerance)}
+ * {@link BaseIntStatisticTest#testAccept(int[], StatisticResult, DoubleTolerance)}
* method.
*
* @return the tolerance
@@ -288,7 +275,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Gets the tolerance for the
- * {@link BaseDoubleStatisticTest#testArray(double[], double, DoubleTolerance)
+ * {@link BaseIntStatisticTest#testArray(int[], StatisticResult, DoubleTolerance)
* )} method.
*
* @return the tolerance
@@ -299,7 +286,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Gets the tolerance for the
- * {@link BaseDoubleStatisticTest#testAcceptAndCombine(double[][], double, DoubleTolerance)}
+ * {@link BaseIntStatisticTest#testAcceptAndCombine(int[][], StatisticResult, DoubleTolerance)}
* method.
*
* @return the tolerance
@@ -310,7 +297,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Gets the tolerance for the
- * {@link BaseDoubleStatisticTest#testArrayAndCombine(double[][], double, DoubleTolerance)}
+ * {@link BaseIntStatisticTest#testArrayAndCombine(int[][], StatisticResult, DoubleTolerance)}
* method.
*
* @return the tolerance
@@ -330,60 +317,39 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
void setup() {
statisticName = getStatisticName();
// Compute the expected value for the standard test data
- Stream.of(V1, V2, V3, V4, V5, V6).forEach(values -> {
- final double[] sample = new double[values.length];
+ Stream.of(V1, V2, V3, V4).forEach(values -> {
+ final int[] sample = new int[values.length];
for (int i = 0; i < sample.length; i++) {
sample[i] = mapValue(values[i]);
}
- final double expected = getExpectedValue(sample);
- data.add(new TestData(sample, expected));
+ data.add(new TestData(values, getExpectedValue(values)));
});
- // Data with non-finite values.
+ // Data with extreme values.
// Note: The order does not matter as this is shuffled
// in the random order tests.
- final double a = Double.NaN;
- final double b = Double.POSITIVE_INFINITY;
- final double c = Double.NEGATIVE_INFINITY;
- final double[][] nonFinite = {
+ final int a = Integer.MAX_VALUE;
+ final int b = Integer.MIN_VALUE;
+ final int[][] extreme = {
{a, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{b, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {c, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{a, a, 1, 1, 1, 1, 1, 1, 1, 1},
{b, b, 1, 1, 1, 1, 1, 1, 1, 1},
- {c, c, 1, 1, 1, 1, 1, 1, 1, 1},
{a, b, 1, 1, 1, 1, 1, 1, 1, 1},
- {a, c, 1, 1, 1, 1, 1, 1, 1, 1},
- {b, c, 1, 1, 1, 1, 1, 1, 1, 1},
- {a, b, c, 1, 1, 1, 1, 1, 1, 1},
};
- Stream.of(nonFinite).forEach(values -> {
- final double[] sample = values.clone();
+ Stream.of(extreme).forEach(values -> {
+ final int[] sample = values.clone();
for (int i = sample.length; i-- > 0;) {
- if (!Double.isFinite(values[i])) {
+ if (values[i] == 1) {
break;
}
sample[i] = mapValue(sample[i]);
}
- final double expected = getExpectedNonFiniteValue(sample);
- dataNonFinite.add(new TestData(sample, expected));
+ dataExtremeValue.add(new TestData(values, getExpectedValue(values)));
});
// Cache the custom test data so any computed expected values are calculated only once
streamTestData().forEach(dataCustom::add);
}
- /**
- * Gets the statistic name.
- *
- * <p>The default implementation removes the text {@code "Test"} from the
- * simple class name.
- *
- * @return the distribution name
- * @see Class#getSimpleName()
- */
- protected String getStatisticName() {
- return getClass().getSimpleName().replace("Test", "");
- }
-
/**
* Creates the statistic.
*
@@ -397,44 +363,20 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param values Values.
* @return the statistic
*/
- protected abstract S create(double... values);
-
- /**
- * Get the maximum number of values that can be added where the statistic is
- * considered empty.
- *
- * <p>The default is zero. Override this method if the statistic requires multiple
- * values to compute.
- *
- * @return the empty size
- * @see #getEmptyValue()
- */
- protected int getEmptySize() {
- return 0;
- }
-
- /**
- * Gets the value of the statistics when it is considered empty.
- *
- * @return the empty value
- * @see #getEmptySize()
- */
- protected abstract double getEmptyValue();
+ protected abstract S create(int... values);
/**
* Map the {@code value} to the valid domain of the statistic. This method is called
- * with the example data before {@link #getExpectedValue(double[])} and
- * {@link #getExpectedNonFiniteValue(double[])}. In the later case the method is only
- * called with finite value. It can be used by implementing classes to adjust the data
- * to a valid domain, for example if computing the sum-of-logs all values can be
- * updated to be strictly positive.
+ * with the example data before {@link #getExpectedValue(int[])}. It can be used by
+ * implementing classes to adjust the data to a valid domain, for example if computing
+ * the sum-of-logs all values can be updated to be strictly positive.
*
* <p>The default implementation does nothing.
*
* @param value Value
* @return the mapped value
*/
- protected double mapValue(double value) {
+ protected int mapValue(int value) {
return value;
}
@@ -444,166 +386,71 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* <p>This method should be implemented using the canoncial definition of the statistic,
* ideally in high-precision (e.g. using BigDecimal for statistics involving arithmetic).
*
- * <p>This method will not be called with non-finite values.
- *
* @param values Values.
* @return the expected value
*/
- protected abstract double getExpectedValue(double[] values);
+ protected abstract StatisticResult getExpectedValue(int[] values);
/**
- * Gets the expected value of the statistic from the {@code values}, at least one
- * of which will be non-finite. This method will be called with arrays that contain
- * combinations of {@code NaN} and {@code +/-infinity}.
+ * Creates the equivalent {@link DoubleStatistic} from the {@code values}.
+ * This is used to cross-validate the {@link IntStatistic} result.
*
* @param values Values.
- * @return the expected value
- */
- protected abstract double getExpectedNonFiniteValue(double[] values);
-
- /**
- * Gets the tolerance for equality of the statistic and the expected value.
- * This method is not used by the base implementation except to provide the default
- * implementation for the individual test tolerance methods.
- *
- * <p><strong>Note</strong>
- *
- * <p>The default implementation throws an exception. This forces the
- * implementation to either provide a single tolerance for all tests, or the tests
- * individually by overriding each tolerance method.
- *
- * @return the tolerance
- */
- protected DoubleTolerance getTolerance() {
- throw new IllegalStateException("Not all test tolerances have been configured");
- }
-
- /**
- * Gets the tolerance for equality of the statistic and the expected value
- * for the {@link #testAccept(double[], double, DoubleTolerance)} test.
- *
- * <p>The default implementation uses {@link #getTolerance()}.
- *
- * @return the tolerance
- */
- protected DoubleTolerance getToleranceAccept() {
- return getTolerance();
- }
-
- /**
- * Gets the tolerance for equality of the statistic and the expected value
- * for the {@link #testArray(double[], double, DoubleTolerance)} test.
- *
- * <p>The default implementation uses {@link #getTolerance()}.
- *
- * @return the tolerance
- */
- protected DoubleTolerance getToleranceArray() {
- return getTolerance();
- }
-
- /**
- * Gets the tolerance for equality of the statistic and the expected value
- * for the {@link #testAcceptAndCombine(double[][], double, DoubleTolerance)} test.
- *
- * <p>The default implementation uses {@link #getTolerance()}.
- *
- * @return the tolerance
+ * @return the statistic
*/
- protected DoubleTolerance getToleranceAcceptAndCombine() {
- return getTolerance();
- }
+ protected abstract DoubleStatistic createAsDoubleStatistic(int... values);
- /**
- * Gets the tolerance for equality of the statistic and the expected value
- * for the {@link #testArrayAndCombine(double[][], double, DoubleTolerance)} test.
- *
- * <p>The default implementation uses {@link #getTolerance()}.
- *
- * @return the tolerance
- */
- protected DoubleTolerance getToleranceArrayAndCombine() {
- return getTolerance();
- }
+ //------------------------ Helper Methods to create test data ---------------------------
/**
- * Gets the tolerance for equality of the statistic and the expected value
- * for any test that includes data with non-finite values.
- *
- * <p>The default implementation uses binary equality.
- *
- * <p>Note: This tolerance has not been separated for different tests that use
- * the {@code accept}, {@link #create(double...)} and {@code combine} methods.
- * Computation of a statistic from non-finite values should produce either
- * {@code NaN} or {@code +/-infinity}. However this is not enforced as the expected
- * value is computed by implementations.
+ * Creates the test data.
+ * The expected value must be provided.
*
- * @return the tolerance
- * {@link #getExpectedNonFiniteValue(double[])}
- */
- protected DoubleTolerance getToleranceNonFinite() {
- return DoubleTolerances.equals();
- }
-
- /**
- * Returns {@code true} if the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
- * method is symmetric. If {@code true} then the combine will be tested with
- * duplicate instances of the statistic, combined in left-to-right and right-to-left;
- * the result must have the same statistic value.
+ * <p>This method can be used to add data computed with external reference implementations.
*
- * <p>The default is {@code true}. Override this if the implementation is not symmetric
- * due to the implementation details.
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code int}.
*
- * @return {@code true} if combine is symmetric
+ * @param expected Expected statistic value.
+ * @param values Values.
+ * @return the statistic test data
*/
- protected boolean isCombineSymmetric() {
- return true;
+ final StatisticTestData addReference(int expected, int... values) {
+ Assertions.assertEquals(ResultType.INT, getResultType());
+ return new StatisticTestData(values, createStatisticResult(expected));
}
- //------------------------ Helper Methods to create test data ---------------------------
-
/**
- * Creates the tolerance using an absolute error.
- *
- * <p>If the absolute tolerance is zero it is ignored and a tolerance of numerical
- * equality is used.
+ * Creates the test data.
+ * The expected value must be provided.
*
- * @param eps Absolute tolerance
- * @return the tolerance
- */
- static final DoubleTolerance createAbsTolerance(double eps) {
- return eps > 0 ? DoubleTolerances.absolute(eps) : DoubleTolerances.ulps(0);
- }
-
- /**
- * Creates the tolerance using an relative error.
+ * <p>This method can be used to add data computed with external reference implementations.
*
- * <p>If the relative tolerance is zero it is ignored and a tolerance of numerical
- * equality is used.
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code long}.
*
- * @param eps Relative tolerance
- * @return the tolerance
+ * @param expected Expected statistic value.
+ * @param values Values.
+ * @return the statistic test data
*/
- static final DoubleTolerance createRelTolerance(double eps) {
- return eps > 0 ? DoubleTolerances.relative(eps) : DoubleTolerances.ulps(0);
+ final StatisticTestData addReference(long expected, int... values) {
+ Assertions.assertEquals(ResultType.LONG, getResultType());
+ return new StatisticTestData(values, createStatisticResult(expected));
}
/**
- * Creates the tolerance using an {@code Or} combination of absolute and relative error.
+ * Creates the test data.
+ * The expected value must be provided.
*
- * <p>If the absolute tolerance is zero it is ignored and a tolerance of numerical equality
- * is used.
+ * <p>This method can be used to add data computed with external reference implementations.
*
- * <p>If the relative tolerance is zero it is ignored.
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code BigInteger}.
*
- * @param absTolerance Absolute tolerance
- * @param relTolerance Relative tolerance
- * @return the tolerance
+ * @param expected Expected statistic value.
+ * @param values Values.
+ * @return the statistic test data
*/
- static final DoubleTolerance createAbsOrRelTolerance(double absTolerance, double relTolerance) {
- final DoubleTolerance tol = createAbsTolerance(absTolerance);
- return relTolerance > 0 ? tol.or(DoubleTolerances.relative(relTolerance)) : tol;
+ final StatisticTestData addReference(BigInteger expected, int... values) {
+ Assertions.assertEquals(ResultType.BIG_INTEGER, getResultType());
+ return new StatisticTestData(values, createStatisticResult(expected));
}
/**
@@ -613,6 +460,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*
* <p>This method can be used to add data computed with external reference implementations.
*
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code double}.
+ *
* @param expected Expected statistic value.
* @param values Values.
* @return the statistic test data
@@ -621,8 +470,9 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @see #getToleranceAcceptAndCombine()
* @see #getToleranceArrayAndCombine()
*/
- final StatisticTestData addReference(double expected, double... values) {
- return new StatisticTestData(values, expected,
+ StatisticTestData addReference(double expected, int... values) {
+ Assertions.assertEquals(ResultType.DOUBLE, getResultType());
+ return new StatisticTestData(values, createStatisticResult(expected),
getToleranceAccept(), getToleranceArray(),
getToleranceAcceptAndCombine(), getToleranceArrayAndCombine());
}
@@ -633,13 +483,16 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*
* <p>This method can be used to add data computed with external reference implementations.
*
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code double}.
+ *
* @param expected Expected statistic value.
* @param tol Test tolerance.
* @param values Values.
* @return the statistic test data
*/
- static final StatisticTestData addReference(double expected, DoubleTolerance tol, double... values) {
- return new StatisticTestData(values, expected, tol);
+ final StatisticTestData addReference(double expected, DoubleTolerance tol, int... values) {
+ Assertions.assertEquals(ResultType.DOUBLE, getResultType());
+ return new StatisticTestData(values, createStatisticResult(expected), tol);
}
/**
@@ -648,6 +501,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*
* <p>This method can be used to add data computed with external reference implementations.
*
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code double}.
+ *
* @param expected Expected statistic value.
* @param tolAccept Tolerance for the test of the statistic using the accept method.
* @param tolArray Tolerance for the test of the statistic using the array create method.
@@ -656,11 +511,12 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param values Values.
* @return the statistic test data
*/
- static final StatisticTestData addReference(double expected,
+ final StatisticTestData addReference(double expected,
DoubleTolerance tolAccept, DoubleTolerance tolArray,
DoubleTolerance tolAcceptCombine, DoubleTolerance tolArrayCombine,
- double... values) {
- return new StatisticTestData(values, expected,
+ int... values) {
+ Assertions.assertEquals(ResultType.DOUBLE, getResultType());
+ return new StatisticTestData(values, createStatisticResult(expected),
tolAccept, tolArray, tolAcceptCombine, tolArrayCombine);
}
@@ -673,38 +529,46 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*
* @param values Values.
* @return the statistic test data
- * @see #getExpectedValue(double[])
+ * @see #getExpectedValue(int[])
* @see #getToleranceAccept()
* @see #getToleranceArray()
* @see #getToleranceAcceptAndCombine()
* @see #getToleranceArrayAndCombine()
*/
- final StatisticTestData addCase(double... values) {
- return new StatisticTestData(values, getExpectedValue(values),
- getToleranceAccept(), getToleranceArray(),
- getToleranceAcceptAndCombine(), getToleranceArrayAndCombine());
+ final StatisticTestData addCase(int... values) {
+ if (getResultType() == ResultType.DOUBLE) {
+ return new StatisticTestData(values, getExpectedValue(values),
+ getToleranceAccept(), getToleranceArray(),
+ getToleranceAcceptAndCombine(), getToleranceArrayAndCombine());
+ }
+ return new StatisticTestData(values, getExpectedValue(values));
}
/**
* Creates the edge-case/example test data. The same tolerance is used for all tests.
- * The expected value will be computed using {@link #getExpectedValue(double[])}.
+ * The expected value will be computed using {@link #getExpectedValue(int[])}.
*
* <p>This method can be used to add data to target specific edge cases.
*
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code double}.
+ *
* @param tol Test tolerance.
* @param values Values.
* @return the statistic test data
*/
- final StatisticTestData addCase(DoubleTolerance tol, double... values) {
+ final StatisticTestData addCase(DoubleTolerance tol, int... values) {
+ Assertions.assertEquals(ResultType.DOUBLE, getResultType());
return new StatisticTestData(values, getExpectedValue(values), tol);
}
/**
* Creates the edge-case/example test data.
- * The expected value will be computed using {@link #getExpectedValue(double[])}.
+ * The expected value will be computed using {@link #getExpectedValue(int[])}.
*
* <p>This method can be used to add data to target specific edge cases.
*
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code double}.
+ *
* @param tolAccept Tolerance for the test of the statistic using the accept method.
* @param tolArray Tolerance for the test of the statistic using the array create method.
* @param tolAcceptCombine Tolerance for the test of the statistic using the accept and combine methods.
@@ -715,7 +579,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
final StatisticTestData addCase(
DoubleTolerance tolAccept, DoubleTolerance tolArray,
DoubleTolerance tolAcceptCombine, DoubleTolerance tolArrayCombine,
- double... values) {
+ int... values) {
+ Assertions.assertEquals(ResultType.DOUBLE, getResultType());
return new StatisticTestData(values, getExpectedValue(values),
tolAccept, tolArray, tolAcceptCombine, tolArrayCombine);
}
@@ -738,12 +603,12 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* will be used for all 4 main tests of the statistic:
*
* <ol>
- * <li>{@link java.util.function.DoubleConsumer#accept accept}
- * <li>{@link java.util.function.DoubleConsumer#accept accept} and
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
- * <li>{@link #create(double...)}
- * <li>{@link #create(double...)} and
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * <li>{@link java.util.function.IntConsumer#accept accept}
+ * <li>{@link java.util.function.IntConsumer#accept accept} and
+ * {@link StatisticAccumulator#combine(StatisticResult) combine}
+ * <li>{@link #create(int...)}
+ * <li>{@link #create(int...)} and
+ * {@link StatisticAccumulator#combine(StatisticResult) combine}
* </ol>
*
* <p>To test the {@code combine} method the data is split into approximately even groups
@@ -759,7 +624,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* add(3.5, createRelTolerance(1e-15), 2, 3, 4, 5),
* add(11.5, createRelTolerance(1e-15), 2, 3, 4, 5, 13, 42),
* // Edge case for mean
- * add(createRelTolerance(1e-15), Double.MAX_VALUE, -Double.MAX_VALUE)
+ * add(createRelTolerance(1e-15), Int.MAX_VALUE, -Int.MAX_VALUE)
* )}
* </pre>
*
@@ -769,20 +634,11 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*/
protected abstract Stream<StatisticTestData> streamTestData();
- /**
- * Gets the group sizes used to test the combine method with the custom test data.
- *
- * @return the group sizes
- */
- protected int[] getCombineGroupSizes() {
- return new int[] {2, 3};
- }
-
//------------------------ Methods to stream the test data -----------------------------
/**
* Stream the arguments to test the computation of the statistic using the
- * {@link java.util.function.DoubleConsumer#accept accept} method. The expected value
+ * {@link java.util.function.IntConsumer#accept accept} method. The expected value
* and tolerance are supplied by the implementing class.
*
* @return the stream
@@ -793,7 +649,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Stream the arguments to test the computation of the statistic using the
- * {@link #create(double...)} method. The expected value and tolerance are supplied
+ * {@link #create(int...)} method. The expected value and tolerance are supplied
* by the implementing class.
*
* @return the stream
@@ -804,7 +660,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Stream the arguments to test the computation of the statistic using a single
- * {@code double[]} array.
+ * {@code int[]} array.
*
* @param tol Test tolerance.
* @param tolCustom Tolerance for any custom test data.
@@ -814,15 +670,15 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
Function<StatisticTestData, DoubleTolerance> tolCustom) {
final Builder<Arguments> b = Stream.builder();
data.forEach(d -> b.accept(Arguments.of(d.getValues(), d.getExpected(), tol)));
- dataNonFinite.forEach(d -> b.accept(Arguments.of(d.getValues(), d.getExpected(), getToleranceNonFinite())));
+ dataExtremeValue.forEach(d -> b.accept(Arguments.of(d.getValues(), d.getExpected(), tol)));
dataCustom.forEach(d -> b.accept(Arguments.of(d.getValues(), d.getExpected(), tolCustom.apply(d))));
return b.build();
}
/**
* Stream the arguments to test the computation of the statistic using the
- * {@link java.util.function.DoubleConsumer#accept(double) accept} method for each
- * array, then the {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * {@link java.util.function.IntConsumer#accept(int) accept} method for each
+ * array, then the {@link StatisticAccumulator#combine(StatisticResult) combine}
* method. The expected value and tolerance are supplied by the implementing class.
*
* @return the stream
@@ -833,8 +689,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Stream the arguments to test the computation of the statistic using the
- * {@link #create(double...)} method for each array, then the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method. The
+ * {@link #create(int...)} method for each array, then the
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method. The
* expected value and tolerance are supplied by the implementing class.
*
* @return the stream
@@ -845,9 +701,9 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Stream the arguments to test the computation of the statistic using the
- * {@link java.util.function.DoubleConsumer#accept(double) accept} method for each
+ * {@link java.util.function.IntConsumer#accept(int) accept} method for each
* element of a parallel stream, then the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method.
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method.
* The expected value and tolerance are supplied by the implementing class.
*
* <p>Note that this method uses the tolerance from {@link #getToleranceAcceptAndCombine()}.
@@ -860,8 +716,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Stream the arguments to test the computation of the statistic using the multiple
- * {@code double[]} arrays and the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method.
+ * {@code int[]} arrays and the
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method.
*
* @param tol Test tolerance.
* @param tolCustom Tolerance for any custom test data.
@@ -876,15 +732,15 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
.map(x -> cut(d.getValues(), x))
.forEach(e -> b.accept(Arguments.of(e, d.getExpected(), tol)));
});
- dataNonFinite.forEach(d -> {
+ dataExtremeValue.forEach(d -> {
// Cut the standard data into pieces
- Arrays.stream(SIZES_NON_FINITE)
+ Arrays.stream(SIZES_EXTREME)
.map(x -> cut(d.getValues(), x))
- .forEach(e -> b.accept(Arguments.of(e, d.getExpected(), getToleranceNonFinite())));
+ .forEach(e -> b.accept(Arguments.of(e, d.getExpected(), tol)));
});
dataCustom.forEach(d -> {
// Only split lengths above the number of groups
- final double[] values = d.getValues();
+ final int[] values = d.getValues();
final int n = values.length;
for (final int groups : getCombineGroupSizes()) {
if (groups <= n) {
@@ -905,8 +761,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param sizes Sizes.
* @return the set of arrays
*/
- private static double[][] cut(double[] data, int... sizes) {
- ArrayList<double[]> set = new ArrayList<>();
+ private static int[][] cut(int[] data, int... sizes) {
+ ArrayList<int[]> set = new ArrayList<>();
int from = 0;
for (final int size : sizes) {
final int to = Math.min(data.length, from + size);
@@ -922,7 +778,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
Assertions.fail(
() -> "Sizes " + Arrays.toString(sizes) + " do not cover length " + data.length);
}
- return set.toArray(new double[0][]);
+ return set.toArray(new int[0][]);
}
/**
@@ -933,14 +789,14 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param groups Number of groups.
* @return the set of arrays
*/
- private static double[][] split(double[] data, int groups) {
+ private static int[][] split(int[] data, int groups) {
// Note: round the size of the group.
// The final group accounts for length difference:
// 4/3 => 1, 1, 2
// 5/3 => 2, 2, 1
// 7/3 => 2, 2, 3
final int size = (int) Math.round((double) data.length / groups);
- final double[][] values = new double[groups][];
+ final int[][] values = new int[groups][];
for (int i = 0; i < groups; i++) {
final int from = i * size;
// Final group may be smaller or larger
@@ -975,106 +831,122 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param tol Non-empty tolerance.
*/
private void assertEmpty(S stat, DoubleTolerance tol) {
- final double v = getEmptyValue();
+ final StatisticResult v = getEmptyValue();
final int size = getEmptySize();
- final double[] values = new double[size + 1];
+ final int[] values = new int[size + 1];
// Fill beyond empty
for (int i = 0; i <= size; i++) {
- Assertions.assertEquals(v, stat.getAsDouble(), () -> statisticName + " should be empty");
+ TestHelper.assertEquals(v, stat, tol, () -> statisticName + " should be empty");
stat.accept(i + 1);
values[i] = i + 1;
}
- final double expected = getExpectedValue(values);
- TestUtils.assertEquals(expected, stat.getAsDouble(), tol,
+ final StatisticResult expected = getExpectedValue(values);
+ TestHelper.assertEquals(expected, stat, tol,
() -> statisticName + " should not be empty");
}
/**
* Test the computation of the statistic using the
- * {@link java.util.function.DoubleConsumer#accept(double) accept} method. The
+ * {@link java.util.function.IntConsumer#accept(int) accept} method. The
* statistic is created using both the {@link #create()} and the
- * {@link #create(double...)} methods; the two instances must compute the same result.
+ * {@link #create(int...)} methods; the two instances must compute the same result.
*/
@ParameterizedTest
@MethodSource
- final void testAccept(double[] values, double expected, DoubleTolerance tol) {
- final double actual = assertStatistic(v -> Statistics.add(create(), v), values, expected, tol);
- // Repeat after creation with an empty array
- assertStatistic(v -> Statistics.add(create(EMPTY), v), values, actual, tol);
+ final void testAccept(int[] values, StatisticResult expected, DoubleTolerance tol) {
+ final S actual = assertStatistic(v -> Statistics.add(create(), v), values, expected, tol);
+ // Repeat after creation with an empty array. No tolerance for exact equality.
+ assertStatistic(v -> Statistics.add(create(EMPTY), v), values, actual, null);
}
/**
- * Test the computation of the statistic using the {@link #create(double...)} method.
+ * Test the computation of the statistic using the {@link #create(int...)} method.
*/
@ParameterizedTest
@MethodSource
- final void testArray(double[] values, double expected, DoubleTolerance tol) {
+ final void testArray(int[] values, StatisticResult expected, DoubleTolerance tol) {
assertStatistic(this::create, values, expected, tol);
}
+ /**
+ * Test the computation of the statistic against the equivalent {@link DoubleStatistic}.
+ * The result is tested as a {@code double} using the configured {@link #getToleranceAsDouble()}.
+ * Integer values are not asserted as they may not be an exact match.
+ */
+ @ParameterizedTest
+ @MethodSource(value = {"testAccept"})
+ final void testVsDoubleStatistic(int[] values) {
+ final double expected = createAsDoubleStatistic(values).getAsDouble();
+ final DoubleTolerance tol = getToleranceAsDouble();
+ TestUtils.assertEquals(expected, Statistics.add(create(), values).getAsDouble(), tol,
+ () -> statisticName + " accept: " + format(values));
+ TestUtils.assertEquals(expected, create(values).getAsDouble(), tol,
+ () -> statisticName + " array: " + format(values));
+ }
+
/**
* Test the computation of the statistic using the
- * {@link java.util.function.DoubleConsumer#accept(double) accept} method for each
- * array, then the {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * {@link java.util.function.IntConsumer#accept(int) accept} method for each
+ * array, then the {@link StatisticAccumulator#combine(StatisticResult) combine}
* method.
*/
@ParameterizedTest
@MethodSource
- final void testAcceptAndCombine(double[][] values, double expected, DoubleTolerance tol) {
+ final void testAcceptAndCombine(int[][] values, StatisticResult expected, DoubleTolerance tol) {
assertCombine(v -> Statistics.add(create(), v), values, expected, tol);
}
/**
* Test the computation of the statistic using the
- * {@link java.util.function.DoubleConsumer#accept(double) accept} method for each
- * array, then the {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * {@link java.util.function.IntConsumer#accept(int) accept} method for each
+ * array, then the {@link StatisticAccumulator#combine(StatisticResult) combine}
* method.
*/
@ParameterizedTest
@MethodSource
- final void testArrayAndCombine(double[][] values, double expected, DoubleTolerance tol) {
+ final void testArrayAndCombine(int[][] values, StatisticResult expected, DoubleTolerance tol) {
assertCombine(this::create, values, expected, tol);
}
/**
- * Test the {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method
+ * Test the {@link StatisticAccumulator#combine(StatisticResult) combine} method
* with an empty instance combined with a non-empty instance.
*/
@ParameterizedTest
@MethodSource(value = "testAccept")
- final void testEmptyCombine(double[] values) {
+ final void testEmptyCombine(int[] values) {
final S empty = create();
final S nonEmpty = Statistics.add(create(), values);
- final double expected = nonEmpty.getAsDouble();
+ final StatisticResult expected = Statistics.add(create(), values);
final S result = assertCombine(empty, nonEmpty);
- Assertions.assertEquals(expected, result.getAsDouble(),
+ TestHelper.assertEquals(expected, result, null,
() -> statisticName + " empty.combine(nonEmpty)");
}
/**
- * Test the {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method
+ * Test the {@link StatisticAccumulator#combine(StatisticResult) combine} method
* with a non-empty instance combined with an empty instance.
*/
@ParameterizedTest
@MethodSource(value = "testAccept")
- final void testCombineEmpty(double[] values) {
+ final void testCombineEmpty(int[] values) {
final S empty = create();
final S nonEmpty = Statistics.add(create(), values);
- final double expected = nonEmpty.getAsDouble();
+ final StatisticResult expected = Statistics.add(create(), values);
final S result = assertCombine(nonEmpty, empty);
- Assertions.assertEquals(expected, result.getAsDouble(),
+ TestHelper.assertEquals(expected, result, null,
() -> statisticName + " nonEmpty.combine(empty)");
}
/**
* Test the computation of the statistic using a parallel stream of {@code double}
* values. The accumulator is the
- * {@link java.util.function.DoubleConsumer#accept(double) accept} method; the
- * combiner is the {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * {@link java.util.function.IntConsumer#accept(int) accept} method; the
+ * combiner is the {@link StatisticAccumulator#combine(StatisticResult) combine}
* method.
*
* <p>Note: This method is similar to the
- * {@link #testAcceptAndCombine(double[][], double, DoubleTolerance)} method and uses
+ * {@link #testAcceptAndCombine(int[][], StatisticResult, DoubleTolerance)} method and uses
* the same data, concatenated to a single array, and tolerance. The difference is the
* former method uses explicit subsets of the data and its own fixed algorithm to
* combine instances. This method leaves the entire process of dividing the data and
@@ -1083,22 +955,21 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*/
@ParameterizedTest
@MethodSource
- final void testAcceptParallelStream(double[] values, double expected, DoubleTolerance tol) {
- final double actual = Arrays.stream(values)
+ final void testAcceptParallelStream(int[] values, StatisticResult expected, DoubleTolerance tol) {
+ final S actual = Arrays.stream(values)
.parallel()
- .collect(this::create, DoubleStatistic::accept, DoubleStatisticAccumulator::combine)
- .getAsDouble();
- TestUtils.assertEquals(expected, actual, tol, () -> statisticName + ": " + format(values));
+ .collect(this::create, IntStatistic::accept, StatisticAccumulator::combine);
+ TestHelper.assertEquals(expected, actual, tol, () -> statisticName + ": " + format(values));
}
/**
- * Test the computation of the statistic using a parallel stream of {@code double[]}
- * arrays. The arrays are mapped to a statistic using the {@link #create(double...)}
+ * Test the computation of the statistic using a parallel stream of {@code int[]}
+ * arrays. The arrays are mapped to a statistic using the {@link #create(int...)}
* method, and the stream reduced using the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method.
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method.
*
* <p>Note: This method is similar to the
- * {@link #testArrayAndCombine(double[][], double, DoubleTolerance)} method and uses
+ * {@link #testArrayAndCombine(int[][], StatisticResult, DoubleTolerance)} method and uses
* the same data and tolerance. The difference is the former method uses its own fixed
* algorithm to combine instances. This method leaves the process of combining to the
* JDK parallel stream; results may vary across platforms as it is dependent on the
@@ -1106,24 +977,23 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*/
@ParameterizedTest
@MethodSource(value = "testArrayAndCombine")
- final void testArrayParallelStream(double[][] values, double expected, DoubleTolerance tol) {
- final double actual = Arrays.stream(values)
+ final void testArrayParallelStream(int[][] values, StatisticResult expected, DoubleTolerance tol) {
+ final S actual = Arrays.stream(values)
.parallel()
.map(this::create)
- .reduce(DoubleStatisticAccumulator::combine)
+ .reduce(StatisticAccumulator::combine)
// Return an empty instance if there is no data
- .orElseGet(this::create)
- .getAsDouble();
- TestUtils.assertEquals(expected, actual, tol, () -> statisticName + ": " + format(values));
+ .orElseGet(this::create);
+ TestHelper.assertEquals(expected, actual, tol, () -> statisticName + ": " + format(values));
}
/**
- * Invokes the {@link #testAccept(double[], double, DoubleTolerance)} method using
+ * Invokes the {@link #testAccept(int[], StatisticResult, DoubleTolerance)} method using
* random permutations of the data.
*/
@ParameterizedTest
@MethodSource(value = "testAccept")
- final void testAcceptRandom(double[] values, double expected, DoubleTolerance tol) {
+ final void testAcceptRandom(int[] values, StatisticResult expected, DoubleTolerance tol) {
// Obtain a seed so that it can be logged to allow repeats
final long[] seed = TestHelper.createRNGSeed();
final UniformRandomProvider rng = TestHelper.createRNG(seed);
@@ -1138,12 +1008,12 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
}
/**
- * Invokes the {@link #testArray(double[], double, DoubleTolerance)} method using
+ * Invokes the {@link #testArray(int[], StatisticResult, DoubleTolerance)} method using
* random permutations of the data.
*/
@ParameterizedTest
@MethodSource(value = "testArray")
- final void testArrayRandom(double[] values, double expected, DoubleTolerance tol) {
+ final void testArrayRandom(int[] values, StatisticResult expected, DoubleTolerance tol) {
// Obtain a seed so that it can be logged to allow repeats
final long[] seed = TestHelper.createRNGSeed();
final UniformRandomProvider rng = TestHelper.createRNG(seed);
@@ -1158,16 +1028,16 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
}
/**
- * Invokes the {@link #testAcceptAndCombine(double[][], double, DoubleTolerance)} method using
+ * Invokes the {@link #testAcceptAndCombine(int[][], StatisticResult, DoubleTolerance)} method using
* random permutations of the data.
*/
@ParameterizedTest
@MethodSource(value = "testAcceptAndCombine")
- final void testAcceptAndCombineRandom(double[][] values, double expected, DoubleTolerance tol) {
+ final void testAcceptAndCombineRandom(int[][] values, StatisticResult expected, DoubleTolerance tol) {
// Obtain a seed so that it can be logged to allow repeats
final long[] seed = TestHelper.createRNGSeed();
final UniformRandomProvider rng = TestHelper.createRNG(seed);
- final double[] allValues = TestHelper.concatenate(values);
+ final int[] allValues = TestHelper.concatenate(values);
int repeat = 0;
try {
while (repeat++ < RANDOM_PERMUTATIONS) {
@@ -1181,16 +1051,16 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
}
/**
- * Invokes the {@link #testArrayAndCombine(double[][], double, DoubleTolerance)} method using
+ * Invokes the {@link #testArrayAndCombine(int[][], StatisticResult, DoubleTolerance)} method using
* random permutations of the data.
*/
@ParameterizedTest
@MethodSource(value = "testArrayAndCombine")
- final void testArrayAndCombineRandom(double[][] values, double expected, DoubleTolerance tol) {
+ final void testArrayAndCombineRandom(int[][] values, StatisticResult expected, DoubleTolerance tol) {
// Obtain a seed so that it can be logged to allow repeats
final long[] seed = TestHelper.createRNGSeed();
final UniformRandomProvider rng = TestHelper.createRNG(seed);
- final double[] allValues = TestHelper.concatenate(values);
+ final int[] allValues = TestHelper.concatenate(values);
int repeat = 0;
try {
while (repeat++ < RANDOM_PERMUTATIONS) {
@@ -1205,14 +1075,14 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Test the computation of the statistic using an empty instance and the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method with
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method with
* instances containing single values. Adding statistic instances containing 1 value
* should be the same precision as adding a {@code double} value, although the two
* results are not required to be exactly equal.
*/
@ParameterizedTest
@MethodSource(value = "testAccept")
- final void testCombineAsAccept(double[] values, double expected, DoubleTolerance tol) {
+ final void testCombineAsAccept(int[] values, StatisticResult expected, DoubleTolerance tol) {
final S empty = create();
assertStatistic(v ->
Arrays.stream(values)
@@ -1230,17 +1100,16 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param tol Test tolerance.
* @return the computed statistic
*/
- final double assertStatistic(Function<double[], S> constructor,
- double[] values, double expected, DoubleTolerance tol) {
+ final S assertStatistic(Function<int[], S> constructor,
+ int[] values, StatisticResult expected, DoubleTolerance tol) {
final S stat = constructor.apply(values);
- final double actual = stat.getAsDouble();
- TestUtils.assertEquals(expected, actual, tol, () -> statisticName + ": " + format(values));
- return actual;
+ TestHelper.assertEquals(expected, stat, tol, () -> statisticName + ": " + format(values));
+ return stat;
}
/**
* Assert the computation of the statistic using the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method.
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method.
*
* @param constructor Function to create the statistic.
* @param values Values.
@@ -1248,8 +1117,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param tol Test tolerance.
* @return the computed statistic
*/
- final double assertCombine(Function<double[], S> constructor,
- double[][] values, double expected, DoubleTolerance tol) {
+ final S assertCombine(Function<int[], S> constructor,
+ int[][] values, StatisticResult expected, DoubleTolerance tol) {
int n = values.length;
Assertions.assertNotEquals(0, n, "No data");
final List<S> stats = Arrays.stream(values)
@@ -1288,9 +1157,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
n >>= 1;
}
final S stat = stats.get(0);
- final double actual = stat.getAsDouble();
- TestUtils.assertEquals(expected, actual, tol, () -> statisticName + ": " + format(values));
- return actual;
+ TestHelper.assertEquals(expected, stat, tol, () -> statisticName + ": " + format(values));
+ return stat;
}
/**
@@ -1311,43 +1179,23 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
stats.set(target, s1);
if (isCombineSymmetric()) {
final S s2 = assertCombine(stats2.get(rhs), stats2.get(lhs));
- Assertions.assertEquals(s1.getAsDouble(), s2.getAsDouble(),
+ TestHelper.assertEquals(s1, s2, null,
() -> statisticName + ": Non-symmetric combine");
stats2.set(target, s2);
}
}
- /**
- * Combine the two statistics. This method asserts the contract of the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method.
- * The left-hand side (LHS) argument must be returned. The right-hand side (RHS) must
- * be unchanged by the operation.
- *
- * <p>Use this method whenever combining two instances of a statistic.
- *
- * @param a LHS.
- * @param b RHS.
- * @return the result
- */
- final S assertCombine(S a, S b) {
- final double before = b.getAsDouble();
- final S c = a.combine(b);
- Assertions.assertEquals(before, b.getAsDouble(), () -> statisticName + ": Combine altered the RHS");
- Assertions.assertSame(a, c, () -> statisticName + ": Combined did not return the LHS");
- return c;
- }
-
/**
* Format the values as a string. The maximum length is limited.
*
* @param values Values.
* @return the string
*/
- static String format(double[] values) {
+ static String format(int[] values) {
if (values.length > MAX_FORMAT_VALUES) {
return Arrays.stream(values)
.limit(MAX_FORMAT_VALUES)
- .mapToObj(Double::toString)
+ .mapToObj(Integer::toString)
.collect(Collectors.joining(", ", "[", ", ...]"));
}
return Arrays.toString(values);
@@ -1359,9 +1207,9 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param values Values.
* @return the string
*/
- private static String format(double[][] values) {
+ private static String format(int[][] values) {
return Arrays.stream(values)
- .map(BaseDoubleStatisticTest::format)
+ .map(BaseIntStatisticTest::format)
.collect(Collectors.joining(", "));
}
@@ -1373,7 +1221,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param seed Seed.
* @param repeat Repeat of the total random permutations.
*/
- private void rethrowWithSeedAndRepeat(AssertionError e, long[] seed, int repeat) {
+ private static void rethrowWithSeedAndRepeat(AssertionError e, long[] seed, int repeat) {
throw new AssertionError(String.format("%s; Seed=%s; Repeat=%d/%d",
e.getMessage(), Arrays.toString(seed), repeat, RANDOM_PERMUTATIONS), e);
}
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseDoubleStatisticTest.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseLongStatisticTest.java
similarity index 60%
copy from commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseDoubleStatisticTest.java
copy to commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseLongStatisticTest.java
index 6e32115..c3a567d 100644
--- a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseDoubleStatisticTest.java
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseLongStatisticTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.commons.statistics.descriptive;
+import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -26,7 +27,6 @@ import java.util.stream.Stream;
import java.util.stream.Stream.Builder;
import org.apache.commons.rng.UniformRandomProvider;
import org.apache.commons.statistics.distribution.DoubleTolerance;
-import org.apache.commons.statistics.distribution.DoubleTolerances;
import org.apache.commons.statistics.distribution.TestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@@ -39,36 +39,43 @@ import org.junit.jupiter.params.provider.MethodSource;
/**
* Abstract base class for statistic tests.
- * This supports testing statistics that implement {@link DoubleStatistic} and
- * {@link DoubleStatisticAccumulator}.
+ * This supports testing statistics that implement {@link LongStatistic} and
+ * {@link StatisticAccumulator}.
*
* <p>This class uses parameterized tests that are repeated for instances of a
- * statistic. The statistic is tested using standard finite and non-finite data.
+ * statistic. The statistic is tested using standard data.
* Additional data may be provided by implementations.
*
* <p>There are 4 main tests for a statistic populated with values using:
*
* <ol>
- * <li>{@link java.util.function.DoubleConsumer#accept accept}
- * <li>{@link java.util.function.DoubleConsumer#accept accept} and
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
- * <li>{@link #create(double...)}
- * <li>{@link #create(double...)} and
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * <li>{@link java.util.function.LongConsumer#accept accept}
+ * <li>{@link java.util.function.LongConsumer#accept accept} and
+ * {@link StatisticAccumulator#combine(StatisticResult) combine}
+ * <li>{@link #create(long...)}
+ * <li>{@link #create(long...)} and
+ * {@link StatisticAccumulator#combine(StatisticResult) combine}
* </ol>
*
- * <p>An test implementation has to provide these method implementations:
+ * <p>Note: Integer statistics may return the expected result as a {@code double}, {@code int},
+ * {@code long}, or {@code BigInteger}. The default is {@code double}.
+ *
+ * <p>A test implementation has to provide these method implementations:
*
* <ul>
+ * <li>{@link #getResultType()}: Get the native result type of the statistic.
+ * This is used to verify that test cases are added using the correct type and
+ * test tolerances are configured for {@code double} result types.
* <li>{@link #create()}: Create an empty statistic.
- * <li>{@link #create(double...)}: Create a statistic from a set of values.
+ * <li>{@link #create(int...)}: Create a statistic from a set of values.
* <li>{@link #getEmptyValue()}: The expected value of a statistic when not enough values have
* been observed. The minimum number of values can be provided in {@link #getEmptySize()}.
- * <li>{@link #getExpectedValue(double[])}: A method to compute an expected value for the
+ * <li>{@link #getExpectedValue(long[])}: A method to compute an expected value for the
* statistic. This is used to create the expected value on standard test data. It should
* be an implementation of the canonical definition of the statistic. It can use extended
* precision to provided an accurate expected result.
- * <li>{@link #getTolerance()}: The test tolerance for all tests. This method is not used
+ * <li>{@link #getTolerance()}: The test tolerance for all tests when the expected value is
+ * a {@code double}. Tolerances are not used for integer results. This method is not used
* directly by the base class for test cases. It provides the default implementation for
* the 4 methods to provide the tolerance for each of the 4 main test cases.
* <em>Note that the default implementation throws an exception</em>. This forces the
@@ -77,12 +84,6 @@ import org.junit.jupiter.params.provider.MethodSource;
* precision for creation of the statistic from a stream of values or an array then
* it can override {@link #getToleranceAccept()}, {@link #getToleranceArray()} and
* their combine equivalents.
- * <li>{@link #getExpectedNonFiniteValue(double[])}: A method to compute an expected value
- * for the statistic when a non-finite value is encountered. This is only used with at
- * least one non-finite value. Typically this should return {@code NaN} or an infinity.
- * Data tested with non-finite values will use {@link #getToleranceNonFinite()} which
- * defaults to an exact binary equality. This may be overridden for example to return
- * a tolerance that matches any non-finite value.
* <li>{@link #streamTestData()}: Provide custom data for the test. It is
* <em>strongly recommended</em> to provide reference data from independent implementations.
* Edge case data for the statistic can also be provided. See the method javadoc for
@@ -96,7 +97,7 @@ import org.junit.jupiter.params.provider.MethodSource;
* The default uses the class name without the "Test" suffix.
* <li>{@link #isCombineSymmetric()}: Specify whether to test {@code a.combine(b)} is exactly
* equal to {@code b.combine(a)}. The default is {@code true}.
- * <li>{@link #mapValue(double)}: A method to update the sample data to the valid domain
+ * <li>{@link #mapValue(int)}: A method to update the sample data to the valid domain
* for the statistic. This can be used to alter the default test data, for example
* by mapping any negative values to positive.
* </ul>
@@ -107,66 +108,36 @@ import org.junit.jupiter.params.provider.MethodSource;
* @param <S> Statistic type
*/
@TestInstance(Lifecycle.PER_CLASS)
-abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatisticAccumulator<S>> {
- /** An empty {@code double[]}. */
- private static final double[] EMPTY = {};
- /** The number of random permutations to perform. */
- private static final int RANDOM_PERMUTATIONS = 5;
- /** The maximum number of values to output in assertion messages. This is used to provide
- * the data for the failed test which is useful to identify the failed test case. */
- private static final int MAX_FORMAT_VALUES = 100;
+abstract class BaseLongStatisticTest<S extends LongStatistic & StatisticAccumulator<S>>
+ extends BaseStatisticTest {
+ /** An empty {@code long[]}. */
+ private static final long[] EMPTY = {};
// Standard test data samples using SciPy version 1.11:
// import scipy.stats
/** Uniform samples: scipy.stats.randint.rvs(-15, 25, size=100).
* https://en.wikipedia.org/wiki/Discrete_uniform_distribution */
- private static final double[] V1 = {20, 8, 1, 2, 24, -13, -1, 19, 0, 23, 14, 2, -9, 0, 7, -15, -2, -9, -1, 12, -12,
+ private static final long[] V1 = {20, 8, 1, 2, 24, -13, -1, 19, 0, 23, 14, 2, -9, 0, 7, -15, -2, -9, -1, 12, -12,
9, 2, 12, 0, 5, 18, 15, -10, 6, 14, 6, 24, 12, -2, 1, -9, 17, 10, 7, 4, -8, 7, 7, -9, 13, -13, 0, -13, 6, 10,
-6, 23, 2, 6, -10, 12, 6, -6, 4, -5, -9, 5, 16, -14, -4, 22, -4, 20, 12, -13, 6, -13, 12, -3, 13, 1, -6, 13,
-14, -8, 4, 24, 10, 22, 8, -3, -5, 6, 9, 2, 16, 20, -8, -5, 23, 19, 20, -10, 21};
/** Poisson samples: scipy.stats.poisson.rvs(1.5, size=100).
* https://en.wikipedia.org/wiki/Poisson_distribution */
- private static final double[] V2 = {3, 0, 3, 4, 0, 1, 1, 3, 2, 1, 3, 1, 5, 0, 0, 0, 3, 3, 2, 0, 3, 1, 2, 1, 1, 0, 0,
+ private static final long[] V2 = {3, 0, 3, 4, 0, 1, 1, 3, 2, 1, 3, 1, 5, 0, 0, 0, 3, 3, 2, 0, 3, 1, 2, 1, 1, 0, 0,
1, 0, 1, 3, 0, 0, 2, 2, 0, 2, 1, 2, 1, 3, 2, 1, 2, 0, 2, 2, 1, 2, 0, 3, 2, 0, 1, 2, 2, 1, 2, 1, 0, 2, 0, 2, 2,
3, 2, 1, 1, 0, 1, 2, 1, 0, 0, 6, 2, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 2, 1, 2, 4, 2, 4, 2, 0, 0, 1, 1, 2, 2, 3};
/** Poisson samples: scipy.stats.poisson.rvs(4.5, size=100). */
- private static final double[] V3 = {1, 5, 5, 2, 1, 7, 5, 5, 3, 5, 5, 2, 4, 4, 6, 2, 7, 5, 3, 6, 7, 3, 5, 3, 7, 6, 4,
+ private static final long[] V3 = {1, 5, 5, 2, 1, 7, 5, 5, 3, 5, 5, 2, 4, 4, 6, 2, 7, 5, 3, 6, 7, 3, 5, 3, 7, 6, 4,
3, 3, 3, 3, 3, 5, 3, 2, 8, 2, 4, 3, 1, 2, 5, 4, 3, 5, 4, 4, 8, 6, 2, 4, 3, 5, 5, 6, 3, 1, 6, 8, 6, 3, 6, 10, 4,
5, 2, 4, 2, 2, 8, 2, 2, 4, 1, 4, 2, 5, 3, 2, 4, 4, 6, 9, 4, 5, 9, 9, 6, 3, 5, 3, 3, 5, 7, 5, 2, 3, 7, 4, 5};
/** Poisson samples: scipy.stats.poisson.rvs(45, size=100). */
- private static final double[] V4 = {42, 51, 38, 38, 49, 48, 42, 47, 51, 46, 45, 35, 39, 42, 49, 55, 53, 46, 49, 56,
+ private static final long[] V4 = {42, 51, 38, 38, 49, 48, 42, 47, 51, 46, 45, 35, 39, 42, 49, 55, 53, 46, 49, 56,
42, 46, 42, 53, 43, 55, 49, 52, 51, 45, 40, 49, 39, 40, 46, 43, 46, 48, 36, 44, 40, 49, 49, 43, 45, 44, 41, 55,
52, 45, 57, 41, 43, 44, 38, 52, 44, 45, 43, 42, 38, 37, 47, 42, 47, 45, 70, 45, 50, 47, 46, 50, 47, 35, 43, 52,
51, 41, 45, 42, 45, 53, 46, 48, 51, 43, 63, 48, 49, 41, 58, 51, 59, 43, 39, 32, 35, 46, 50, 50};
- /** Normal samples: scipy.stats.norm.rvs(loc=3.4, scale=2.25, size=100). */
- private static final double[] V5 = {1.06356579, -1.52552007, 7.09739891, -0.41516549, 0.17131653, 0.77923148,
- 2.90491862, 4.12648256, 5.04920689, 4.20053484, 5.83485097, 4.33138009, 4.18795702, 3.269289, 2.2399589,
- 4.16551591, -1.67192439, 1.44919254, 3.52270229, -1.49186865, -0.30794835, 5.82394621, 4.84755567, 4.79622486,
- 5.12461983, 2.62561931, 5.12457788, 8.24460895, 4.91249002, 3.75550863, 4.35440479, 4.17587334, -0.34934393,
- 2.98071452, -1.35620308, 1.93956508, 7.57171999, 5.41976186, 2.8427556, 3.04101193, 2.20374721, 4.65406057,
- 5.76961878, 3.14412957, 7.60322297, 1.598286, 2.51552974, 0.67767289, 0.76514432, 3.65663671, 0.53116457,
- 2.79439061, 7.58564809, 4.16735822, 2.95210392, 6.37867376, 6.57010411, 0.11837698, 9.16270054, 3.80097588,
- 5.48811672, 3.83378268, 2.03669252, 5.34865676, 3.11338528, 4.70088345, 6.00069684, 0.16144587, 4.22654482,
- 2.2722623, 5.39142224, 0.811471, 2.74523433, 6.32457234, 0.73033045, 9.54402353, 0.4800466, 2.00806359,
- 6.06115109, 2.3072464, 5.40974674, 2.05533169, 0.97160161, 8.06915145, 4.40792026, 4.53139251, 3.32350119,
- 1.53645238, 3.49059212, 3.57904997, 0.58634639, 5.87567911, 3.49424866, 5.72228178, 4.41403447, 1.27815121,
- 7.13861948, 4.68209093, 6.4598438, 0.66270586};
- /** Gamma samples: scipy.stats.gamma.rvs(9, scale=0.5, size=100). */
- private static final double[] V6 = {3.46479451, 8.80950045, 3.91437318, 4.23327834, 2.6910161, 4.51122052,
- 5.81939474, 3.9142699, 7.75537607, 6.06693317, 3.29388792, 3.90689471, 3.26357137, 3.6398822, 5.60048428,
- 3.68248997, 5.09297897, 4.6302593, 7.01654777, 4.2244833, 2.75326355, 5.36988549, 2.88392811, 3.50131464,
- 4.81183009, 4.92155284, 4.37061644, 3.8064197, 3.31941113, 5.01257676, 3.48037207, 2.62777255, 6.2447332,
- 6.18425783, 3.06915179, 6.42851381, 3.8969583, 3.48723372, 3.49516941, 2.90404439, 2.25920041, 3.68515649,
- 5.09607663, 3.18984299, 2.49261713, 3.9345895, 6.01480539, 8.8065787, 4.3464082, 5.03522483, 4.05315513,
- 6.07365399, 4.34804323, 5.16061656, 3.24706079, 2.89888437, 6.24575902, 3.10893227, 3.74196045, 3.94099137,
- 3.33951846, 4.9264514, 5.21935748, 5.06904776, 2.77543623, 3.72451685, 6.35546017, 4.42425655, 5.99568005,
- 4.2602446, 3.75834066, 5.17730802, 3.60682583, 4.09703419, 5.59942582, 3.49191032, 3.02164323, 8.85183547,
- 5.58631958, 3.24891648, 4.03267796, 4.30984912, 3.78187375, 5.98969913, 1.68855026, 8.43117397, 3.808258,
- 4.82043745, 2.91020117, 4.12921107, 3.54350667, 4.60545934, 5.63180941, 5.07331453, 5.04419517, 3.78796082,
- 4.25143811, 6.3242129, 4.25630677, 4.59158821};
-
- /** Sizes to cut the sample data into chunks. The {@link #cut(double[], int...)} method is robust
+
+ /** Sizes to cut the sample data into chunks. The {@link #cut(long[], int...)} method is robust
* to the sizes summing to more than the sample length. If they sum below then the expected
* value may be incorrect as all data is not used. These all sum to 100 which imposes a maximum
* length on sample data. */
@@ -180,40 +151,43 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
};
- /** Sizes to cut the non-finite sample data into chunks.
+ /** Sizes to cut the extreme value sample data into chunks.
* These all sum to 10 which imposes a maximum length on sample data. */
- private static final int[][] SIZES_NON_FINITE = {
+ private static final int[][] SIZES_EXTREME = {
{5, 5},
{3, 3, 4},
{2, 2, 2, 2, 2},
};
+ /** The statistic name. */
+ private String statisticName;
+
/** The test data and expected values. */
private final List<TestData> data = new ArrayList<>();
- /** The non-finite test data and expected values. */
- private final List<TestData> dataNonFinite = new ArrayList<>();
+ /** The extreme value test data and expected values. */
+ private final List<TestData> dataExtremeValue = new ArrayList<>();
/** Cache of the custom test data. */
private final List<StatisticTestData> dataCustom = new ArrayList<>();
- /** The statistic name. */
- private String statisticName;
-
/**
* Container for test data.
+ *
+ * <p>The statistics is expected to be either a {@code double}, {@code int}, {@code long}
+ * of {@code BigInteger} value.
*/
private static class TestData {
/** The sample values. */
- private final double[] values;
+ private final long[] values;
/** The expected statistic value. */
- private final Double expected;
+ private final StatisticResult expected;
/**
- * @param expected Expected statistic value.
* @param values Sample values.
+ * @param expected Expected statistic value.
*/
- TestData(double[] values, double expected) {
+ TestData(long[] values, StatisticResult expected) {
this.values = values;
- this.expected = Double.valueOf(expected);
+ this.expected = expected;
}
/**
@@ -221,7 +195,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*
* @return the values
*/
- double[] getValues() {
+ long[] getValues() {
return values.clone();
}
@@ -230,13 +204,14 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*
* @return the expected
*/
- Double getExpected() {
+ StatisticResult getExpected() {
return expected;
}
}
/**
- * Container for test data with tolerances for the tests.
+ * Container for test data with tolerances for the tests. Tolerances are used only
+ * for expected values as a {@code double}.
*/
static class StatisticTestData extends TestData {
/** The tolerance for the accept test. */
@@ -248,12 +223,24 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/** The tolerance for the array+combine test. */
private final DoubleTolerance tolArrayCombine;
+ /**
+ * @param values Sample values.
+ * @param expected Expected statistic value.
+ */
+ StatisticTestData(long[] values, StatisticResult expected) {
+ super(values, expected);
+ this.tolAccept = null;
+ this.tolArray = null;
+ this.tolAcceptCombine = null;
+ this.tolArrayCombine = null;
+ }
+
/**
* @param values Sample values.
* @param expected Expected statistic value.
* @param tol Tolerance for the tests.
*/
- StatisticTestData(double[] values, double expected, DoubleTolerance tol) {
+ StatisticTestData(long[] values, StatisticResult expected, DoubleTolerance tol) {
this(values, expected, tol, tol, tol, tol);
}
@@ -265,7 +252,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param tolAcceptCombine Tolerance for the test of the statistic using the accept and combine methods.
* @param tolArrayCombine Tolerance for the test of the statistic using the array create and combine methods.
*/
- StatisticTestData(double[] values, double expected,
+ StatisticTestData(long[] values, StatisticResult expected,
DoubleTolerance tolAccept, DoubleTolerance tolArray,
DoubleTolerance tolAcceptCombine, DoubleTolerance tolArrayCombine) {
super(values, expected);
@@ -277,7 +264,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Gets the tolerance for the
- * {@link BaseDoubleStatisticTest#testAccept(double[], double, DoubleTolerance)}
+ * {@link BaseLongStatisticTest#testAccept(long[], StatisticResult, DoubleTolerance)}
* method.
*
* @return the tolerance
@@ -288,7 +275,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Gets the tolerance for the
- * {@link BaseDoubleStatisticTest#testArray(double[], double, DoubleTolerance)
+ * {@link BaseLongStatisticTest#testArray(long[], StatisticResult, DoubleTolerance)
* )} method.
*
* @return the tolerance
@@ -299,7 +286,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Gets the tolerance for the
- * {@link BaseDoubleStatisticTest#testAcceptAndCombine(double[][], double, DoubleTolerance)}
+ * {@link BaseLongStatisticTest#testAcceptAndCombine(long[][], StatisticResult, DoubleTolerance)}
* method.
*
* @return the tolerance
@@ -310,7 +297,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Gets the tolerance for the
- * {@link BaseDoubleStatisticTest#testArrayAndCombine(double[][], double, DoubleTolerance)}
+ * {@link BaseLongStatisticTest#testArrayAndCombine(long[][], StatisticResult, DoubleTolerance)}
* method.
*
* @return the tolerance
@@ -330,60 +317,39 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
void setup() {
statisticName = getStatisticName();
// Compute the expected value for the standard test data
- Stream.of(V1, V2, V3, V4, V5, V6).forEach(values -> {
- final double[] sample = new double[values.length];
+ Stream.of(V1, V2, V3, V4).forEach(values -> {
+ final long[] sample = new long[values.length];
for (int i = 0; i < sample.length; i++) {
sample[i] = mapValue(values[i]);
}
- final double expected = getExpectedValue(sample);
- data.add(new TestData(sample, expected));
+ data.add(new TestData(values, getExpectedValue(values)));
});
- // Data with non-finite values.
+ // Data with extreme values.
// Note: The order does not matter as this is shuffled
// in the random order tests.
- final double a = Double.NaN;
- final double b = Double.POSITIVE_INFINITY;
- final double c = Double.NEGATIVE_INFINITY;
- final double[][] nonFinite = {
+ final int a = Integer.MAX_VALUE;
+ final int b = Integer.MIN_VALUE;
+ final long[][] extreme = {
{a, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{b, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {c, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{a, a, 1, 1, 1, 1, 1, 1, 1, 1},
{b, b, 1, 1, 1, 1, 1, 1, 1, 1},
- {c, c, 1, 1, 1, 1, 1, 1, 1, 1},
{a, b, 1, 1, 1, 1, 1, 1, 1, 1},
- {a, c, 1, 1, 1, 1, 1, 1, 1, 1},
- {b, c, 1, 1, 1, 1, 1, 1, 1, 1},
- {a, b, c, 1, 1, 1, 1, 1, 1, 1},
};
- Stream.of(nonFinite).forEach(values -> {
- final double[] sample = values.clone();
+ Stream.of(extreme).forEach(values -> {
+ final long[] sample = values.clone();
for (int i = sample.length; i-- > 0;) {
- if (!Double.isFinite(values[i])) {
+ if (values[i] == 1) {
break;
}
sample[i] = mapValue(sample[i]);
}
- final double expected = getExpectedNonFiniteValue(sample);
- dataNonFinite.add(new TestData(sample, expected));
+ dataExtremeValue.add(new TestData(values, getExpectedValue(values)));
});
// Cache the custom test data so any computed expected values are calculated only once
streamTestData().forEach(dataCustom::add);
}
- /**
- * Gets the statistic name.
- *
- * <p>The default implementation removes the text {@code "Test"} from the
- * simple class name.
- *
- * @return the distribution name
- * @see Class#getSimpleName()
- */
- protected String getStatisticName() {
- return getClass().getSimpleName().replace("Test", "");
- }
-
/**
* Creates the statistic.
*
@@ -397,44 +363,20 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param values Values.
* @return the statistic
*/
- protected abstract S create(double... values);
-
- /**
- * Get the maximum number of values that can be added where the statistic is
- * considered empty.
- *
- * <p>The default is zero. Override this method if the statistic requires multiple
- * values to compute.
- *
- * @return the empty size
- * @see #getEmptyValue()
- */
- protected int getEmptySize() {
- return 0;
- }
-
- /**
- * Gets the value of the statistics when it is considered empty.
- *
- * @return the empty value
- * @see #getEmptySize()
- */
- protected abstract double getEmptyValue();
+ protected abstract S create(long... values);
/**
* Map the {@code value} to the valid domain of the statistic. This method is called
- * with the example data before {@link #getExpectedValue(double[])} and
- * {@link #getExpectedNonFiniteValue(double[])}. In the later case the method is only
- * called with finite value. It can be used by implementing classes to adjust the data
- * to a valid domain, for example if computing the sum-of-logs all values can be
- * updated to be strictly positive.
+ * with the example data before {@link #getExpectedValue(long[])}. It can be used by
+ * implementing classes to adjust the data to a valid domain, for example if computing
+ * the sum-of-logs all values can be updated to be strictly positive.
*
* <p>The default implementation does nothing.
*
* @param value Value
* @return the mapped value
*/
- protected double mapValue(double value) {
+ protected long mapValue(long value) {
return value;
}
@@ -444,166 +386,71 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* <p>This method should be implemented using the canoncial definition of the statistic,
* ideally in high-precision (e.g. using BigDecimal for statistics involving arithmetic).
*
- * <p>This method will not be called with non-finite values.
- *
* @param values Values.
* @return the expected value
*/
- protected abstract double getExpectedValue(double[] values);
+ protected abstract StatisticResult getExpectedValue(long[] values);
/**
- * Gets the expected value of the statistic from the {@code values}, at least one
- * of which will be non-finite. This method will be called with arrays that contain
- * combinations of {@code NaN} and {@code +/-infinity}.
+ * Creates the equivalent {@link DoubleStatistic} from the {@code values}.
+ * This is used to cross-validate the {@link LongStatistic} result.
*
* @param values Values.
- * @return the expected value
- */
- protected abstract double getExpectedNonFiniteValue(double[] values);
-
- /**
- * Gets the tolerance for equality of the statistic and the expected value.
- * This method is not used by the base implementation except to provide the default
- * implementation for the individual test tolerance methods.
- *
- * <p><strong>Note</strong>
- *
- * <p>The default implementation throws an exception. This forces the
- * implementation to either provide a single tolerance for all tests, or the tests
- * individually by overriding each tolerance method.
- *
- * @return the tolerance
- */
- protected DoubleTolerance getTolerance() {
- throw new IllegalStateException("Not all test tolerances have been configured");
- }
-
- /**
- * Gets the tolerance for equality of the statistic and the expected value
- * for the {@link #testAccept(double[], double, DoubleTolerance)} test.
- *
- * <p>The default implementation uses {@link #getTolerance()}.
- *
- * @return the tolerance
- */
- protected DoubleTolerance getToleranceAccept() {
- return getTolerance();
- }
-
- /**
- * Gets the tolerance for equality of the statistic and the expected value
- * for the {@link #testArray(double[], double, DoubleTolerance)} test.
- *
- * <p>The default implementation uses {@link #getTolerance()}.
- *
- * @return the tolerance
- */
- protected DoubleTolerance getToleranceArray() {
- return getTolerance();
- }
-
- /**
- * Gets the tolerance for equality of the statistic and the expected value
- * for the {@link #testAcceptAndCombine(double[][], double, DoubleTolerance)} test.
- *
- * <p>The default implementation uses {@link #getTolerance()}.
- *
- * @return the tolerance
+ * @return the statistic
*/
- protected DoubleTolerance getToleranceAcceptAndCombine() {
- return getTolerance();
- }
+ protected abstract DoubleStatistic createAsDoubleStatistic(long... values);
- /**
- * Gets the tolerance for equality of the statistic and the expected value
- * for the {@link #testArrayAndCombine(double[][], double, DoubleTolerance)} test.
- *
- * <p>The default implementation uses {@link #getTolerance()}.
- *
- * @return the tolerance
- */
- protected DoubleTolerance getToleranceArrayAndCombine() {
- return getTolerance();
- }
+ //------------------------ Helper Methods to create test data ---------------------------
/**
- * Gets the tolerance for equality of the statistic and the expected value
- * for any test that includes data with non-finite values.
- *
- * <p>The default implementation uses binary equality.
- *
- * <p>Note: This tolerance has not been separated for different tests that use
- * the {@code accept}, {@link #create(double...)} and {@code combine} methods.
- * Computation of a statistic from non-finite values should produce either
- * {@code NaN} or {@code +/-infinity}. However this is not enforced as the expected
- * value is computed by implementations.
+ * Creates the test data.
+ * The expected value must be provided.
*
- * @return the tolerance
- * {@link #getExpectedNonFiniteValue(double[])}
- */
- protected DoubleTolerance getToleranceNonFinite() {
- return DoubleTolerances.equals();
- }
-
- /**
- * Returns {@code true} if the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
- * method is symmetric. If {@code true} then the combine will be tested with
- * duplicate instances of the statistic, combined in left-to-right and right-to-left;
- * the result must have the same statistic value.
+ * <p>This method can be used to add data computed with external reference implementations.
*
- * <p>The default is {@code true}. Override this if the implementation is not symmetric
- * due to the implementation details.
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code int}.
*
- * @return {@code true} if combine is symmetric
+ * @param expected Expected statistic value.
+ * @param values Values.
+ * @return the statistic test data
*/
- protected boolean isCombineSymmetric() {
- return true;
+ final StatisticTestData addReference(int expected, long... values) {
+ Assertions.assertEquals(ResultType.INT, getResultType());
+ return new StatisticTestData(values, createStatisticResult(expected));
}
- //------------------------ Helper Methods to create test data ---------------------------
-
/**
- * Creates the tolerance using an absolute error.
- *
- * <p>If the absolute tolerance is zero it is ignored and a tolerance of numerical
- * equality is used.
+ * Creates the test data.
+ * The expected value must be provided.
*
- * @param eps Absolute tolerance
- * @return the tolerance
- */
- static final DoubleTolerance createAbsTolerance(double eps) {
- return eps > 0 ? DoubleTolerances.absolute(eps) : DoubleTolerances.ulps(0);
- }
-
- /**
- * Creates the tolerance using an relative error.
+ * <p>This method can be used to add data computed with external reference implementations.
*
- * <p>If the relative tolerance is zero it is ignored and a tolerance of numerical
- * equality is used.
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code long}.
*
- * @param eps Relative tolerance
- * @return the tolerance
+ * @param expected Expected statistic value.
+ * @param values Values.
+ * @return the statistic test data
*/
- static final DoubleTolerance createRelTolerance(double eps) {
- return eps > 0 ? DoubleTolerances.relative(eps) : DoubleTolerances.ulps(0);
+ final StatisticTestData addReference(long expected, long... values) {
+ Assertions.assertEquals(ResultType.LONG, getResultType());
+ return new StatisticTestData(values, createStatisticResult(expected));
}
/**
- * Creates the tolerance using an {@code Or} combination of absolute and relative error.
+ * Creates the test data.
+ * The expected value must be provided.
*
- * <p>If the absolute tolerance is zero it is ignored and a tolerance of numerical equality
- * is used.
+ * <p>This method can be used to add data computed with external reference implementations.
*
- * <p>If the relative tolerance is zero it is ignored.
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code BigInteger}.
*
- * @param absTolerance Absolute tolerance
- * @param relTolerance Relative tolerance
- * @return the tolerance
+ * @param expected Expected statistic value.
+ * @param values Values.
+ * @return the statistic test data
*/
- static final DoubleTolerance createAbsOrRelTolerance(double absTolerance, double relTolerance) {
- final DoubleTolerance tol = createAbsTolerance(absTolerance);
- return relTolerance > 0 ? tol.or(DoubleTolerances.relative(relTolerance)) : tol;
+ final StatisticTestData addReference(BigInteger expected, long... values) {
+ Assertions.assertEquals(ResultType.BIG_INTEGER, getResultType());
+ return new StatisticTestData(values, createStatisticResult(expected));
}
/**
@@ -613,6 +460,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*
* <p>This method can be used to add data computed with external reference implementations.
*
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code double}.
+ *
* @param expected Expected statistic value.
* @param values Values.
* @return the statistic test data
@@ -621,8 +470,9 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @see #getToleranceAcceptAndCombine()
* @see #getToleranceArrayAndCombine()
*/
- final StatisticTestData addReference(double expected, double... values) {
- return new StatisticTestData(values, expected,
+ StatisticTestData addReference(double expected, long... values) {
+ Assertions.assertEquals(ResultType.DOUBLE, getResultType());
+ return new StatisticTestData(values, createStatisticResult(expected),
getToleranceAccept(), getToleranceArray(),
getToleranceAcceptAndCombine(), getToleranceArrayAndCombine());
}
@@ -633,13 +483,16 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*
* <p>This method can be used to add data computed with external reference implementations.
*
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code double}.
+ *
* @param expected Expected statistic value.
* @param tol Test tolerance.
* @param values Values.
* @return the statistic test data
*/
- static final StatisticTestData addReference(double expected, DoubleTolerance tol, double... values) {
- return new StatisticTestData(values, expected, tol);
+ final StatisticTestData addReference(double expected, DoubleTolerance tol, long... values) {
+ Assertions.assertEquals(ResultType.DOUBLE, getResultType());
+ return new StatisticTestData(values, createStatisticResult(expected), tol);
}
/**
@@ -648,6 +501,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*
* <p>This method can be used to add data computed with external reference implementations.
*
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code double}.
+ *
* @param expected Expected statistic value.
* @param tolAccept Tolerance for the test of the statistic using the accept method.
* @param tolArray Tolerance for the test of the statistic using the array create method.
@@ -656,11 +511,12 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param values Values.
* @return the statistic test data
*/
- static final StatisticTestData addReference(double expected,
+ final StatisticTestData addReference(double expected,
DoubleTolerance tolAccept, DoubleTolerance tolArray,
DoubleTolerance tolAcceptCombine, DoubleTolerance tolArrayCombine,
- double... values) {
- return new StatisticTestData(values, expected,
+ long... values) {
+ Assertions.assertEquals(ResultType.DOUBLE, getResultType());
+ return new StatisticTestData(values, createStatisticResult(expected),
tolAccept, tolArray, tolAcceptCombine, tolArrayCombine);
}
@@ -673,38 +529,46 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*
* @param values Values.
* @return the statistic test data
- * @see #getExpectedValue(double[])
+ * @see #getExpectedValue(long[])
* @see #getToleranceAccept()
* @see #getToleranceArray()
* @see #getToleranceAcceptAndCombine()
* @see #getToleranceArrayAndCombine()
*/
- final StatisticTestData addCase(double... values) {
- return new StatisticTestData(values, getExpectedValue(values),
- getToleranceAccept(), getToleranceArray(),
- getToleranceAcceptAndCombine(), getToleranceArrayAndCombine());
+ final StatisticTestData addCase(long... values) {
+ if (getResultType() == ResultType.DOUBLE) {
+ return new StatisticTestData(values, getExpectedValue(values),
+ getToleranceAccept(), getToleranceArray(),
+ getToleranceAcceptAndCombine(), getToleranceArrayAndCombine());
+ }
+ return new StatisticTestData(values, getExpectedValue(values));
}
/**
* Creates the edge-case/example test data. The same tolerance is used for all tests.
- * The expected value will be computed using {@link #getExpectedValue(double[])}.
+ * The expected value will be computed using {@link #getExpectedValue(long[])}.
*
* <p>This method can be used to add data to target specific edge cases.
*
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code double}.
+ *
* @param tol Test tolerance.
* @param values Values.
* @return the statistic test data
*/
- final StatisticTestData addCase(DoubleTolerance tol, double... values) {
+ final StatisticTestData addCase(DoubleTolerance tol, long... values) {
+ Assertions.assertEquals(ResultType.DOUBLE, getResultType());
return new StatisticTestData(values, getExpectedValue(values), tol);
}
/**
* Creates the edge-case/example test data.
- * The expected value will be computed using {@link #getExpectedValue(double[])}.
+ * The expected value will be computed using {@link #getExpectedValue(long[])}.
*
* <p>This method can be used to add data to target specific edge cases.
*
+ * <p>Note: Throws an exception if {@link #getResultType()} is not {@code double}.
+ *
* @param tolAccept Tolerance for the test of the statistic using the accept method.
* @param tolArray Tolerance for the test of the statistic using the array create method.
* @param tolAcceptCombine Tolerance for the test of the statistic using the accept and combine methods.
@@ -715,7 +579,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
final StatisticTestData addCase(
DoubleTolerance tolAccept, DoubleTolerance tolArray,
DoubleTolerance tolAcceptCombine, DoubleTolerance tolArrayCombine,
- double... values) {
+ long... values) {
+ Assertions.assertEquals(ResultType.DOUBLE, getResultType());
return new StatisticTestData(values, getExpectedValue(values),
tolAccept, tolArray, tolAcceptCombine, tolArrayCombine);
}
@@ -738,12 +603,12 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* will be used for all 4 main tests of the statistic:
*
* <ol>
- * <li>{@link java.util.function.DoubleConsumer#accept accept}
- * <li>{@link java.util.function.DoubleConsumer#accept accept} and
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
- * <li>{@link #create(double...)}
- * <li>{@link #create(double...)} and
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * <li>{@link java.util.function.LongConsumer#accept accept}
+ * <li>{@link java.util.function.LongConsumer#accept accept} and
+ * {@link StatisticAccumulator#combine(StatisticResult) combine}
+ * <li>{@link #create(int...)}
+ * <li>{@link #create(int...)} and
+ * {@link StatisticAccumulator#combine(StatisticResult) combine}
* </ol>
*
* <p>To test the {@code combine} method the data is split into approximately even groups
@@ -759,7 +624,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* add(3.5, createRelTolerance(1e-15), 2, 3, 4, 5),
* add(11.5, createRelTolerance(1e-15), 2, 3, 4, 5, 13, 42),
* // Edge case for mean
- * add(createRelTolerance(1e-15), Double.MAX_VALUE, -Double.MAX_VALUE)
+ * add(createRelTolerance(1e-15), Int.MAX_VALUE, -Int.MAX_VALUE)
* )}
* </pre>
*
@@ -769,20 +634,11 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*/
protected abstract Stream<StatisticTestData> streamTestData();
- /**
- * Gets the group sizes used to test the combine method with the custom test data.
- *
- * @return the group sizes
- */
- protected int[] getCombineGroupSizes() {
- return new int[] {2, 3};
- }
-
//------------------------ Methods to stream the test data -----------------------------
/**
* Stream the arguments to test the computation of the statistic using the
- * {@link java.util.function.DoubleConsumer#accept accept} method. The expected value
+ * {@link java.util.function.LongConsumer#accept accept} method. The expected value
* and tolerance are supplied by the implementing class.
*
* @return the stream
@@ -793,7 +649,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Stream the arguments to test the computation of the statistic using the
- * {@link #create(double...)} method. The expected value and tolerance are supplied
+ * {@link #create(int...)} method. The expected value and tolerance are supplied
* by the implementing class.
*
* @return the stream
@@ -802,9 +658,25 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
return streamSingleArrayData(getToleranceArray(), StatisticTestData::getTolArray);
}
+ /**
+ * Test the computation of the statistic against the equivalent {@link DoubleStatistic}.
+ * The result is tested as a {@code double} using the configured {@link #getToleranceAsDouble()}.
+ * Integer values are not asserted as they may not be an exact match.
+ */
+ @ParameterizedTest
+ @MethodSource(value = {"testAccept"})
+ final void testVsDoubleStatistic(long[] values) {
+ final double expected = createAsDoubleStatistic(values).getAsDouble();
+ final DoubleTolerance tol = getToleranceAsDouble();
+ TestUtils.assertEquals(expected, Statistics.add(create(), values).getAsDouble(), tol,
+ () -> statisticName + " accept: " + format(values));
+ TestUtils.assertEquals(expected, create(values).getAsDouble(), tol,
+ () -> statisticName + " array: " + format(values));
+ }
+
/**
* Stream the arguments to test the computation of the statistic using a single
- * {@code double[]} array.
+ * {@code long[]} array.
*
* @param tol Test tolerance.
* @param tolCustom Tolerance for any custom test data.
@@ -814,15 +686,15 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
Function<StatisticTestData, DoubleTolerance> tolCustom) {
final Builder<Arguments> b = Stream.builder();
data.forEach(d -> b.accept(Arguments.of(d.getValues(), d.getExpected(), tol)));
- dataNonFinite.forEach(d -> b.accept(Arguments.of(d.getValues(), d.getExpected(), getToleranceNonFinite())));
+ dataExtremeValue.forEach(d -> b.accept(Arguments.of(d.getValues(), d.getExpected(), tol)));
dataCustom.forEach(d -> b.accept(Arguments.of(d.getValues(), d.getExpected(), tolCustom.apply(d))));
return b.build();
}
/**
* Stream the arguments to test the computation of the statistic using the
- * {@link java.util.function.DoubleConsumer#accept(double) accept} method for each
- * array, then the {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * {@link java.util.function.LongConsumer#accept(int) accept} method for each
+ * array, then the {@link StatisticAccumulator#combine(StatisticResult) combine}
* method. The expected value and tolerance are supplied by the implementing class.
*
* @return the stream
@@ -833,8 +705,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Stream the arguments to test the computation of the statistic using the
- * {@link #create(double...)} method for each array, then the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method. The
+ * {@link #create(int...)} method for each array, then the
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method. The
* expected value and tolerance are supplied by the implementing class.
*
* @return the stream
@@ -845,9 +717,9 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Stream the arguments to test the computation of the statistic using the
- * {@link java.util.function.DoubleConsumer#accept(double) accept} method for each
+ * {@link java.util.function.LongConsumer#accept(int) accept} method for each
* element of a parallel stream, then the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method.
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method.
* The expected value and tolerance are supplied by the implementing class.
*
* <p>Note that this method uses the tolerance from {@link #getToleranceAcceptAndCombine()}.
@@ -860,8 +732,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Stream the arguments to test the computation of the statistic using the multiple
- * {@code double[]} arrays and the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method.
+ * {@code long[]} arrays and the
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method.
*
* @param tol Test tolerance.
* @param tolCustom Tolerance for any custom test data.
@@ -876,15 +748,15 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
.map(x -> cut(d.getValues(), x))
.forEach(e -> b.accept(Arguments.of(e, d.getExpected(), tol)));
});
- dataNonFinite.forEach(d -> {
+ dataExtremeValue.forEach(d -> {
// Cut the standard data into pieces
- Arrays.stream(SIZES_NON_FINITE)
+ Arrays.stream(SIZES_EXTREME)
.map(x -> cut(d.getValues(), x))
- .forEach(e -> b.accept(Arguments.of(e, d.getExpected(), getToleranceNonFinite())));
+ .forEach(e -> b.accept(Arguments.of(e, d.getExpected(), tol)));
});
dataCustom.forEach(d -> {
// Only split lengths above the number of groups
- final double[] values = d.getValues();
+ final long[] values = d.getValues();
final int n = values.length;
for (final int groups : getCombineGroupSizes()) {
if (groups <= n) {
@@ -905,8 +777,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param sizes Sizes.
* @return the set of arrays
*/
- private static double[][] cut(double[] data, int... sizes) {
- ArrayList<double[]> set = new ArrayList<>();
+ private static long[][] cut(long[] data, int... sizes) {
+ ArrayList<long[]> set = new ArrayList<>();
int from = 0;
for (final int size : sizes) {
final int to = Math.min(data.length, from + size);
@@ -922,7 +794,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
Assertions.fail(
() -> "Sizes " + Arrays.toString(sizes) + " do not cover length " + data.length);
}
- return set.toArray(new double[0][]);
+ return set.toArray(new long[0][]);
}
/**
@@ -933,14 +805,14 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param groups Number of groups.
* @return the set of arrays
*/
- private static double[][] split(double[] data, int groups) {
+ private static long[][] split(long[] data, int groups) {
// Note: round the size of the group.
// The final group accounts for length difference:
// 4/3 => 1, 1, 2
// 5/3 => 2, 2, 1
// 7/3 => 2, 2, 3
final int size = (int) Math.round((double) data.length / groups);
- final double[][] values = new double[groups][];
+ final long[][] values = new long[groups][];
for (int i = 0; i < groups; i++) {
final int from = i * size;
// Final group may be smaller or larger
@@ -975,106 +847,106 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param tol Non-empty tolerance.
*/
private void assertEmpty(S stat, DoubleTolerance tol) {
- final double v = getEmptyValue();
+ final StatisticResult v = getEmptyValue();
final int size = getEmptySize();
- final double[] values = new double[size + 1];
+ final long[] values = new long[size + 1];
// Fill beyond empty
for (int i = 0; i <= size; i++) {
- Assertions.assertEquals(v, stat.getAsDouble(), () -> statisticName + " should be empty");
+ TestHelper.assertEquals(v, stat, tol, () -> statisticName + " should be empty");
stat.accept(i + 1);
values[i] = i + 1;
}
- final double expected = getExpectedValue(values);
- TestUtils.assertEquals(expected, stat.getAsDouble(), tol,
+ final StatisticResult expected = getExpectedValue(values);
+ TestHelper.assertEquals(expected, stat, tol,
() -> statisticName + " should not be empty");
}
/**
* Test the computation of the statistic using the
- * {@link java.util.function.DoubleConsumer#accept(double) accept} method. The
+ * {@link java.util.function.LongConsumer#accept(int) accept} method. The
* statistic is created using both the {@link #create()} and the
- * {@link #create(double...)} methods; the two instances must compute the same result.
+ * {@link #create(int...)} methods; the two instances must compute the same result.
*/
@ParameterizedTest
@MethodSource
- final void testAccept(double[] values, double expected, DoubleTolerance tol) {
- final double actual = assertStatistic(v -> Statistics.add(create(), v), values, expected, tol);
- // Repeat after creation with an empty array
- assertStatistic(v -> Statistics.add(create(EMPTY), v), values, actual, tol);
+ final void testAccept(long[] values, StatisticResult expected, DoubleTolerance tol) {
+ final S actual = assertStatistic(v -> Statistics.add(create(), v), values, expected, tol);
+ // Repeat after creation with an empty array. No tolerance for exact equality.
+ assertStatistic(v -> Statistics.add(create(EMPTY), v), values, actual, null);
}
/**
- * Test the computation of the statistic using the {@link #create(double...)} method.
+ * Test the computation of the statistic using the {@link #create(int...)} method.
*/
@ParameterizedTest
@MethodSource
- final void testArray(double[] values, double expected, DoubleTolerance tol) {
+ final void testArray(long[] values, StatisticResult expected, DoubleTolerance tol) {
assertStatistic(this::create, values, expected, tol);
}
/**
* Test the computation of the statistic using the
- * {@link java.util.function.DoubleConsumer#accept(double) accept} method for each
- * array, then the {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * {@link java.util.function.LongConsumer#accept(int) accept} method for each
+ * array, then the {@link StatisticAccumulator#combine(StatisticResult) combine}
* method.
*/
@ParameterizedTest
@MethodSource
- final void testAcceptAndCombine(double[][] values, double expected, DoubleTolerance tol) {
+ final void testAcceptAndCombine(long[][] values, StatisticResult expected, DoubleTolerance tol) {
assertCombine(v -> Statistics.add(create(), v), values, expected, tol);
}
/**
* Test the computation of the statistic using the
- * {@link java.util.function.DoubleConsumer#accept(double) accept} method for each
- * array, then the {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * {@link java.util.function.LongConsumer#accept(int) accept} method for each
+ * array, then the {@link StatisticAccumulator#combine(StatisticResult) combine}
* method.
*/
@ParameterizedTest
@MethodSource
- final void testArrayAndCombine(double[][] values, double expected, DoubleTolerance tol) {
+ final void testArrayAndCombine(long[][] values, StatisticResult expected, DoubleTolerance tol) {
assertCombine(this::create, values, expected, tol);
}
/**
- * Test the {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method
+ * Test the {@link StatisticAccumulator#combine(StatisticResult) combine} method
* with an empty instance combined with a non-empty instance.
*/
@ParameterizedTest
@MethodSource(value = "testAccept")
- final void testEmptyCombine(double[] values) {
+ final void testEmptyCombine(long[] values) {
final S empty = create();
final S nonEmpty = Statistics.add(create(), values);
- final double expected = nonEmpty.getAsDouble();
+ final StatisticResult expected = Statistics.add(create(), values);
final S result = assertCombine(empty, nonEmpty);
- Assertions.assertEquals(expected, result.getAsDouble(),
+ TestHelper.assertEquals(expected, result, null,
() -> statisticName + " empty.combine(nonEmpty)");
}
/**
- * Test the {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method
+ * Test the {@link StatisticAccumulator#combine(StatisticResult) combine} method
* with a non-empty instance combined with an empty instance.
*/
@ParameterizedTest
@MethodSource(value = "testAccept")
- final void testCombineEmpty(double[] values) {
+ final void testCombineEmpty(long[] values) {
final S empty = create();
final S nonEmpty = Statistics.add(create(), values);
- final double expected = nonEmpty.getAsDouble();
+ final StatisticResult expected = Statistics.add(create(), values);
final S result = assertCombine(nonEmpty, empty);
- Assertions.assertEquals(expected, result.getAsDouble(),
+ TestHelper.assertEquals(expected, result, null,
() -> statisticName + " nonEmpty.combine(empty)");
}
/**
* Test the computation of the statistic using a parallel stream of {@code double}
* values. The accumulator is the
- * {@link java.util.function.DoubleConsumer#accept(double) accept} method; the
- * combiner is the {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine}
+ * {@link java.util.function.LongConsumer#accept(int) accept} method; the
+ * combiner is the {@link StatisticAccumulator#combine(StatisticResult) combine}
* method.
*
* <p>Note: This method is similar to the
- * {@link #testAcceptAndCombine(double[][], double, DoubleTolerance)} method and uses
+ * {@link #testAcceptAndCombine(long[][], StatisticResult, DoubleTolerance)} method and uses
* the same data, concatenated to a single array, and tolerance. The difference is the
* former method uses explicit subsets of the data and its own fixed algorithm to
* combine instances. This method leaves the entire process of dividing the data and
@@ -1083,22 +955,21 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*/
@ParameterizedTest
@MethodSource
- final void testAcceptParallelStream(double[] values, double expected, DoubleTolerance tol) {
- final double actual = Arrays.stream(values)
+ final void testAcceptParallelStream(long[] values, StatisticResult expected, DoubleTolerance tol) {
+ final S actual = Arrays.stream(values)
.parallel()
- .collect(this::create, DoubleStatistic::accept, DoubleStatisticAccumulator::combine)
- .getAsDouble();
- TestUtils.assertEquals(expected, actual, tol, () -> statisticName + ": " + format(values));
+ .collect(this::create, LongStatistic::accept, StatisticAccumulator::combine);
+ TestHelper.assertEquals(expected, actual, tol, () -> statisticName + ": " + format(values));
}
/**
- * Test the computation of the statistic using a parallel stream of {@code double[]}
- * arrays. The arrays are mapped to a statistic using the {@link #create(double...)}
+ * Test the computation of the statistic using a parallel stream of {@code long[]}
+ * arrays. The arrays are mapped to a statistic using the {@link #create(int...)}
* method, and the stream reduced using the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method.
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method.
*
* <p>Note: This method is similar to the
- * {@link #testArrayAndCombine(double[][], double, DoubleTolerance)} method and uses
+ * {@link #testArrayAndCombine(long[][], StatisticResult, DoubleTolerance)} method and uses
* the same data and tolerance. The difference is the former method uses its own fixed
* algorithm to combine instances. This method leaves the process of combining to the
* JDK parallel stream; results may vary across platforms as it is dependent on the
@@ -1106,24 +977,23 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
*/
@ParameterizedTest
@MethodSource(value = "testArrayAndCombine")
- final void testArrayParallelStream(double[][] values, double expected, DoubleTolerance tol) {
- final double actual = Arrays.stream(values)
+ final void testArrayParallelStream(long[][] values, StatisticResult expected, DoubleTolerance tol) {
+ final S actual = Arrays.stream(values)
.parallel()
.map(this::create)
- .reduce(DoubleStatisticAccumulator::combine)
+ .reduce(StatisticAccumulator::combine)
// Return an empty instance if there is no data
- .orElseGet(this::create)
- .getAsDouble();
- TestUtils.assertEquals(expected, actual, tol, () -> statisticName + ": " + format(values));
+ .orElseGet(this::create);
+ TestHelper.assertEquals(expected, actual, tol, () -> statisticName + ": " + format(values));
}
/**
- * Invokes the {@link #testAccept(double[], double, DoubleTolerance)} method using
+ * Invokes the {@link #testAccept(long[], StatisticResult, DoubleTolerance)} method using
* random permutations of the data.
*/
@ParameterizedTest
@MethodSource(value = "testAccept")
- final void testAcceptRandom(double[] values, double expected, DoubleTolerance tol) {
+ final void testAcceptRandom(long[] values, StatisticResult expected, DoubleTolerance tol) {
// Obtain a seed so that it can be logged to allow repeats
final long[] seed = TestHelper.createRNGSeed();
final UniformRandomProvider rng = TestHelper.createRNG(seed);
@@ -1138,12 +1008,12 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
}
/**
- * Invokes the {@link #testArray(double[], double, DoubleTolerance)} method using
+ * Invokes the {@link #testArray(long[], StatisticResult, DoubleTolerance)} method using
* random permutations of the data.
*/
@ParameterizedTest
@MethodSource(value = "testArray")
- final void testArrayRandom(double[] values, double expected, DoubleTolerance tol) {
+ final void testArrayRandom(long[] values, StatisticResult expected, DoubleTolerance tol) {
// Obtain a seed so that it can be logged to allow repeats
final long[] seed = TestHelper.createRNGSeed();
final UniformRandomProvider rng = TestHelper.createRNG(seed);
@@ -1158,16 +1028,16 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
}
/**
- * Invokes the {@link #testAcceptAndCombine(double[][], double, DoubleTolerance)} method using
+ * Invokes the {@link #testAcceptAndCombine(long[][], StatisticResult, DoubleTolerance)} method using
* random permutations of the data.
*/
@ParameterizedTest
@MethodSource(value = "testAcceptAndCombine")
- final void testAcceptAndCombineRandom(double[][] values, double expected, DoubleTolerance tol) {
+ final void testAcceptAndCombineRandom(long[][] values, StatisticResult expected, DoubleTolerance tol) {
// Obtain a seed so that it can be logged to allow repeats
final long[] seed = TestHelper.createRNGSeed();
final UniformRandomProvider rng = TestHelper.createRNG(seed);
- final double[] allValues = TestHelper.concatenate(values);
+ final long[] allValues = TestHelper.concatenate(values);
int repeat = 0;
try {
while (repeat++ < RANDOM_PERMUTATIONS) {
@@ -1181,16 +1051,16 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
}
/**
- * Invokes the {@link #testArrayAndCombine(double[][], double, DoubleTolerance)} method using
+ * Invokes the {@link #testArrayAndCombine(long[][], StatisticResult, DoubleTolerance)} method using
* random permutations of the data.
*/
@ParameterizedTest
@MethodSource(value = "testArrayAndCombine")
- final void testArrayAndCombineRandom(double[][] values, double expected, DoubleTolerance tol) {
+ final void testArrayAndCombineRandom(long[][] values, StatisticResult expected, DoubleTolerance tol) {
// Obtain a seed so that it can be logged to allow repeats
final long[] seed = TestHelper.createRNGSeed();
final UniformRandomProvider rng = TestHelper.createRNG(seed);
- final double[] allValues = TestHelper.concatenate(values);
+ final long[] allValues = TestHelper.concatenate(values);
int repeat = 0;
try {
while (repeat++ < RANDOM_PERMUTATIONS) {
@@ -1205,14 +1075,14 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
/**
* Test the computation of the statistic using an empty instance and the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method with
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method with
* instances containing single values. Adding statistic instances containing 1 value
* should be the same precision as adding a {@code double} value, although the two
* results are not required to be exactly equal.
*/
@ParameterizedTest
@MethodSource(value = "testAccept")
- final void testCombineAsAccept(double[] values, double expected, DoubleTolerance tol) {
+ final void testCombineAsAccept(long[] values, StatisticResult expected, DoubleTolerance tol) {
final S empty = create();
assertStatistic(v ->
Arrays.stream(values)
@@ -1230,17 +1100,16 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param tol Test tolerance.
* @return the computed statistic
*/
- final double assertStatistic(Function<double[], S> constructor,
- double[] values, double expected, DoubleTolerance tol) {
+ final S assertStatistic(Function<long[], S> constructor,
+ long[] values, StatisticResult expected, DoubleTolerance tol) {
final S stat = constructor.apply(values);
- final double actual = stat.getAsDouble();
- TestUtils.assertEquals(expected, actual, tol, () -> statisticName + ": " + format(values));
- return actual;
+ TestHelper.assertEquals(expected, stat, tol, () -> statisticName + ": " + format(values));
+ return stat;
}
/**
* Assert the computation of the statistic using the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method.
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method.
*
* @param constructor Function to create the statistic.
* @param values Values.
@@ -1248,8 +1117,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param tol Test tolerance.
* @return the computed statistic
*/
- final double assertCombine(Function<double[], S> constructor,
- double[][] values, double expected, DoubleTolerance tol) {
+ final S assertCombine(Function<long[], S> constructor,
+ long[][] values, StatisticResult expected, DoubleTolerance tol) {
int n = values.length;
Assertions.assertNotEquals(0, n, "No data");
final List<S> stats = Arrays.stream(values)
@@ -1288,9 +1157,8 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
n >>= 1;
}
final S stat = stats.get(0);
- final double actual = stat.getAsDouble();
- TestUtils.assertEquals(expected, actual, tol, () -> statisticName + ": " + format(values));
- return actual;
+ TestHelper.assertEquals(expected, stat, tol, () -> statisticName + ": " + format(values));
+ return stat;
}
/**
@@ -1311,43 +1179,23 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
stats.set(target, s1);
if (isCombineSymmetric()) {
final S s2 = assertCombine(stats2.get(rhs), stats2.get(lhs));
- Assertions.assertEquals(s1.getAsDouble(), s2.getAsDouble(),
+ TestHelper.assertEquals(s1, s2, null,
() -> statisticName + ": Non-symmetric combine");
stats2.set(target, s2);
}
}
- /**
- * Combine the two statistics. This method asserts the contract of the
- * {@link DoubleStatisticAccumulator#combine(DoubleStatistic) combine} method.
- * The left-hand side (LHS) argument must be returned. The right-hand side (RHS) must
- * be unchanged by the operation.
- *
- * <p>Use this method whenever combining two instances of a statistic.
- *
- * @param a LHS.
- * @param b RHS.
- * @return the result
- */
- final S assertCombine(S a, S b) {
- final double before = b.getAsDouble();
- final S c = a.combine(b);
- Assertions.assertEquals(before, b.getAsDouble(), () -> statisticName + ": Combine altered the RHS");
- Assertions.assertSame(a, c, () -> statisticName + ": Combined did not return the LHS");
- return c;
- }
-
/**
* Format the values as a string. The maximum length is limited.
*
* @param values Values.
* @return the string
*/
- static String format(double[] values) {
+ static String format(long[] values) {
if (values.length > MAX_FORMAT_VALUES) {
return Arrays.stream(values)
.limit(MAX_FORMAT_VALUES)
- .mapToObj(Double::toString)
+ .mapToObj(Long::toString)
.collect(Collectors.joining(", ", "[", ", ...]"));
}
return Arrays.toString(values);
@@ -1359,9 +1207,9 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param values Values.
* @return the string
*/
- private static String format(double[][] values) {
+ private static String format(long[][] values) {
return Arrays.stream(values)
- .map(BaseDoubleStatisticTest::format)
+ .map(BaseLongStatisticTest::format)
.collect(Collectors.joining(", "));
}
@@ -1373,7 +1221,7 @@ abstract class BaseDoubleStatisticTest<S extends DoubleStatistic & DoubleStatist
* @param seed Seed.
* @param repeat Repeat of the total random permutations.
*/
- private void rethrowWithSeedAndRepeat(AssertionError e, long[] seed, int repeat) {
+ private static void rethrowWithSeedAndRepeat(AssertionError e, long[] seed, int repeat) {
throw new AssertionError(String.format("%s; Seed=%s; Repeat=%d/%d",
e.getMessage(), Arrays.toString(seed), repeat, RANDOM_PERMUTATIONS), e);
}
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseStatisticTest.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseStatisticTest.java
new file mode 100644
index 0000000..f058c84
--- /dev/null
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BaseStatisticTest.java
@@ -0,0 +1,331 @@
+/*
+ * 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.statistics.descriptive;
+
+import java.math.BigInteger;
+import org.apache.commons.statistics.distribution.DoubleTolerance;
+import org.apache.commons.statistics.distribution.DoubleTolerances;
+import org.junit.jupiter.api.Assertions;
+
+/**
+ * Abstract base class for statistic tests.
+ *
+ * <p>This class contains common methods used by type-specific statistic tests.
+ */
+abstract class BaseStatisticTest {
+ /** The number of random permutations to perform. */
+ static final int RANDOM_PERMUTATIONS = 5;
+ /** The maximum number of values to output in assertion messages. This is used to provide
+ * the data for the failed test which is useful to identify the failed test case. */
+ static final int MAX_FORMAT_VALUES = 100;
+ /** A marker return object to indicate that a method for a result type raised an exception. */
+ private static final Object MARKER_OBJECT = new Object();
+
+ /**
+ * Gets the statistic name.
+ *
+ * <p>The default implementation removes the text {@code "Test"} from the
+ * simple class name.
+ *
+ * @return the distribution name
+ * @see Class#getSimpleName()
+ */
+ protected String getStatisticName() {
+ return getClass().getSimpleName().replace("Test", "");
+ }
+
+ /**
+ * Gets the native result type of the statistic.
+ * The default is {@code double}.
+ *
+ * @return the result type
+ */
+ protected ResultType getResultType() {
+ return ResultType.DOUBLE;
+ }
+
+ /**
+ * Get the maximum number of values that can be added where the statistic is
+ * considered empty.
+ *
+ * <p>The default is zero. Override this method if the statistic requires multiple
+ * values to compute.
+ *
+ * @return the empty size
+ * @see #getEmptyValue()
+ */
+ protected int getEmptySize() {
+ return 0;
+ }
+
+ /**
+ * Gets the value of the statistics when it is considered empty.
+ *
+ * @return the empty value
+ * @see #getEmptySize()
+ */
+ protected abstract StatisticResult getEmptyValue();
+
+ /**
+ * Creates the statistic result using an {@code double} value.
+ *
+ * @param value Value.
+ * @return the statistic result
+ */
+ static StatisticResult createStatisticResult(double value) {
+ // This method is included for completeness.
+ // It is a simple lambda to the default StatisticResult interface.
+ return () -> value;
+ }
+
+ /**
+ * Creates the statistic result using an {@code int} value.
+ *
+ * @param value Value.
+ * @return the statistic result
+ */
+ static StatisticResult createStatisticResult(int value) {
+ return (IntStatisticResult) () -> value;
+ }
+
+ /**
+ * Creates the statistic result using a {@code long} value.
+ *
+ * @param value Value.
+ * @return the statistic result
+ */
+ static StatisticResult createStatisticResult(long value) {
+ return (LongStatisticResult) () -> value;
+ }
+
+ /**
+ * Creates the statistic result using a {@code BigInteger} value.
+ *
+ * @param value Value.
+ * @return the statistic result
+ */
+ static StatisticResult createStatisticResult(BigInteger value) {
+ return (BigIntegerStatisticResult) () -> value;
+ }
+
+ /**
+ * Gets the tolerance for equality of the statistic and the expected value as a {@code double}.
+ * This method is not used by the base implementation except to provide the default
+ * implementation for the individual test tolerance methods.
+ *
+ * <p>Tolerances are not required to be configured if the result type is not a {@code double}.
+ *
+ * <p><strong>Note</strong>
+ *
+ * <p>The default implementation throws an exception. This forces the
+ * implementation to either provide a single tolerance for all tests, or the tests
+ * individually by overriding each tolerance method.
+ *
+ * @return the tolerance
+ * @see #getResultType()
+ */
+ protected DoubleTolerance getTolerance() {
+ if (getResultType() == ResultType.DOUBLE) {
+ throw new IllegalStateException("Not all test tolerances have been configured");
+ }
+ // Not required
+ return null;
+ }
+
+ /**
+ * Gets the tolerance for equality of the statistic and the expected value
+ * for the {@link #testAccept(int[], StatisticResult, DoubleTolerance)} test.
+ *
+ * <p>The default implementation uses {@link #getTolerance()}.
+ *
+ * @return the tolerance
+ */
+ protected DoubleTolerance getToleranceAccept() {
+ return getTolerance();
+ }
+
+ /**
+ * Gets the tolerance for equality of the statistic and the expected value
+ * for the {@link #testArray(int[], StatisticResult, DoubleTolerance)} test.
+ *
+ * <p>The default implementation uses {@link #getTolerance()}.
+ *
+ * @return the tolerance
+ */
+ protected DoubleTolerance getToleranceArray() {
+ return getTolerance();
+ }
+
+ /**
+ * Gets the tolerance for equality of the statistic and the expected value
+ * for the {@link #testAcceptAndCombine(int[][], StatisticResult, DoubleTolerance)} test.
+ *
+ * <p>The default implementation uses {@link #getTolerance()}.
+ *
+ * @return the tolerance
+ */
+ protected DoubleTolerance getToleranceAcceptAndCombine() {
+ return getTolerance();
+ }
+
+ /**
+ * Gets the tolerance for equality of the statistic and the expected value
+ * for the {@link #testArrayAndCombine(int[][], StatisticResult, DoubleTolerance)} test.
+ *
+ * <p>The default implementation uses {@link #getTolerance()}.
+ *
+ * @return the tolerance
+ */
+ protected DoubleTolerance getToleranceArrayAndCombine() {
+ return getTolerance();
+ }
+
+ /**
+ * Gets the tolerance for equality of the statistic and the equivalent {@link DoubleStatistic}.
+ * This method is used to cross-validate the statistic computation against the reference
+ * {@code double} implementation.
+ *
+ * <p>The default implementation return {@link #getTolerance()}.
+ *
+ * <p>Note: Computation using {@code double} values may not be as accurate as integer
+ * specialisations. This tolerance can be set appropriately to detect errors, for example
+ * using a relative tolerance of 1e-12.
+ *
+ * @return the tolerance
+ */
+ protected DoubleTolerance getToleranceAsDouble() {
+ return getTolerance();
+ }
+
+ /**
+ * Returns {@code true} if the
+ * {@link StatisticAccumulator#combine(StatisticResult) combine}
+ * method is symmetric. If {@code true} then the combine will be tested with
+ * duplicate instances of the statistic, combined in left-to-right and right-to-left;
+ * the result must have the same statistic value.
+ *
+ * <p>The default is {@code true}. Override this if the implementation is not symmetric
+ * due to the implementation details.
+ *
+ * @return {@code true} if combine is symmetric
+ */
+ protected boolean isCombineSymmetric() {
+ return true;
+ }
+
+ //------------------------ Helper Methods to create test data ---------------------------
+
+ /**
+ * Creates the tolerance using an absolute error.
+ *
+ * <p>If the absolute tolerance is zero it is ignored and a tolerance of numerical
+ * equality is used.
+ *
+ * @param eps Absolute tolerance
+ * @return the tolerance
+ */
+ static final DoubleTolerance createAbsTolerance(double eps) {
+ return eps > 0 ? DoubleTolerances.absolute(eps) : DoubleTolerances.ulps(0);
+ }
+
+ /**
+ * Creates the tolerance using an relative error.
+ *
+ * <p>If the relative tolerance is zero it is ignored and a tolerance of numerical
+ * equality is used.
+ *
+ * @param eps Relative tolerance
+ * @return the tolerance
+ */
+ static final DoubleTolerance createRelTolerance(double eps) {
+ return eps > 0 ? DoubleTolerances.relative(eps) : DoubleTolerances.ulps(0);
+ }
+
+ /**
+ * Creates the tolerance using an {@code Or} combination of absolute and relative error.
+ *
+ * <p>If the absolute tolerance is zero it is ignored and a tolerance of numerical equality
+ * is used.
+ *
+ * <p>If the relative tolerance is zero it is ignored.
+ *
+ * @param absTolerance Absolute tolerance
+ * @param relTolerance Relative tolerance
+ * @return the tolerance
+ */
+ static final DoubleTolerance createAbsOrRelTolerance(double absTolerance, double relTolerance) {
+ final DoubleTolerance tol = createAbsTolerance(absTolerance);
+ return relTolerance > 0 ? tol.or(DoubleTolerances.relative(relTolerance)) : tol;
+ }
+
+ /**
+ * Gets the group sizes used to test the combine method with the custom test data.
+ *
+ * @return the group sizes
+ */
+ protected int[] getCombineGroupSizes() {
+ return new int[] {2, 3};
+ }
+
+ /**
+ * Combine the two statistics. This method asserts the contract of the
+ * {@link StatisticAccumulator#combine(StatisticResult) combine} method.
+ * The left-hand side (LHS) argument must be returned. The right-hand side (RHS) must
+ * be unchanged by the operation.
+ *
+ * <p>Use this method whenever combining two instances of a statistic.
+ *
+ * @param <S> Type of statistic
+ * @param a LHS.
+ * @param b RHS.
+ * @return the result
+ */
+ final <S extends StatisticResult & StatisticAccumulator<S>> S assertCombine(S a, S b) {
+ final Object before = getNativeResult(b);
+ final S c = a.combine(b);
+ Assertions.assertEquals(before, getNativeResult(b), () -> getStatisticName() + ": Combine altered the RHS");
+ Assertions.assertSame(a, c, () -> getStatisticName() + ": Combined did not return the LHS");
+ return c;
+ }
+
+ /**
+ * Gets the native result of the statistic.
+ *
+ * @param s Statistic.
+ * @return the native result
+ * @see #getNativeResult(IntStatistic)
+ */
+ private Object getNativeResult(StatisticResult s) {
+ try {
+ switch (getResultType()) {
+ case DOUBLE:
+ return s.getAsDouble();
+ case LONG:
+ return s.getAsLong();
+ case INT:
+ return s.getAsInt();
+ case BIG_INTEGER:
+ return s.getAsBigInteger();
+ default:
+ break;
+ }
+ } catch (Throwable t) {
+ return MARKER_OBJECT;
+ }
+ throw new IllegalStateException("Unrecognised result type: " + getResultType());
+ }
+}
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BigIntegerStatisticResult.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BigIntegerStatisticResult.java
new file mode 100644
index 0000000..7a30ace
--- /dev/null
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/BigIntegerStatisticResult.java
@@ -0,0 +1,48 @@
+/*
+ * 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.statistics.descriptive;
+
+import java.math.BigInteger;
+
+/**
+ * Represents the {@code BigInteger} result of a statistic computed over a set of values.
+ *
+ * <p>This is a helper interface to map the native type of the expected value to other result types.
+ *
+ * @since 1.1
+ */
+@FunctionalInterface
+interface BigIntegerStatisticResult extends StatisticResult {
+
+ @Override
+ BigInteger getAsBigInteger();
+
+ @Override
+ default double getAsDouble() {
+ return getAsBigInteger().doubleValue();
+ }
+
+ @Override
+ default int getAsInt() {
+ return getAsBigInteger().intValueExact();
+ }
+
+ @Override
+ default long getAsLong() {
+ return getAsBigInteger().longValueExact();
+ }
+}
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMaxTest.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMaxTest.java
new file mode 100644
index 0000000..c8d4fd3
--- /dev/null
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMaxTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.statistics.descriptive;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+import org.apache.commons.statistics.distribution.DoubleTolerance;
+import org.apache.commons.statistics.distribution.DoubleTolerances;
+
+/**
+ * Test for {@link IntMax}.
+ */
+final class IntMaxTest extends BaseIntStatisticTest<IntMax> {
+
+ @Override
+ protected ResultType getResultType() {
+ return ResultType.INT;
+ }
+
+ @Override
+ protected IntMax create() {
+ return IntMax.create();
+ }
+
+ @Override
+ protected IntMax create(int... values) {
+ return IntMax.of(values);
+ }
+
+ @Override
+ protected DoubleStatistic createAsDoubleStatistic(int... values) {
+ return Max.of(Arrays.stream(values).asDoubleStream().toArray());
+ }
+
+ @Override
+ protected DoubleTolerance getToleranceAsDouble() {
+ return DoubleTolerances.equals();
+ }
+
+ @Override
+ protected StatisticResult getEmptyValue() {
+ return createStatisticResult(Integer.MIN_VALUE);
+ }
+
+ @Override
+ protected StatisticResult getExpectedValue(int[] values) {
+ // Use the JDK as a reference implementation
+ final int x = Arrays.stream(values).max().orElse(Integer.MIN_VALUE);
+ return createStatisticResult(x);
+ }
+
+ @Override
+ protected Stream<StatisticTestData> streamTestData() {
+ return Stream.of(
+ addCase(Integer.MAX_VALUE - 1),
+ addCase(Integer.MIN_VALUE + 1)
+ );
+ }
+}
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMinTest.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMinTest.java
new file mode 100644
index 0000000..1485769
--- /dev/null
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMinTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.statistics.descriptive;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+import org.apache.commons.statistics.distribution.DoubleTolerance;
+import org.apache.commons.statistics.distribution.DoubleTolerances;
+
+/**
+ * Test for {@link IntMin}.
+ */
+final class IntMinTest extends BaseIntStatisticTest<IntMin> {
+
+ @Override
+ protected ResultType getResultType() {
+ return ResultType.INT;
+ }
+
+ @Override
+ protected IntMin create() {
+ return IntMin.create();
+ }
+
+ @Override
+ protected IntMin create(int... values) {
+ return IntMin.of(values);
+ }
+
+ @Override
+ protected DoubleStatistic createAsDoubleStatistic(int... values) {
+ return Min.of(Arrays.stream(values).asDoubleStream().toArray());
+ }
+
+ @Override
+ protected DoubleTolerance getToleranceAsDouble() {
+ return DoubleTolerances.equals();
+ }
+
+ @Override
+ protected StatisticResult getEmptyValue() {
+ return createStatisticResult(Integer.MAX_VALUE);
+ }
+
+ @Override
+ protected StatisticResult getExpectedValue(int[] values) {
+ // Use the JDK as a reference implementation
+ final int x = Arrays.stream(values).min().orElse(Integer.MAX_VALUE);
+ return createStatisticResult(x);
+ }
+
+ @Override
+ protected Stream<StatisticTestData> streamTestData() {
+ return Stream.of(
+ addCase(Integer.MAX_VALUE - 1),
+ addCase(Integer.MIN_VALUE + 1)
+ );
+ }
+}
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntStatisticResult.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntStatisticResult.java
new file mode 100644
index 0000000..4b5d2f5
--- /dev/null
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntStatisticResult.java
@@ -0,0 +1,48 @@
+/*
+ * 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.statistics.descriptive;
+
+import java.math.BigInteger;
+
+/**
+ * Represents the {@code int} result of a statistic computed over a set of values.
+ *
+ * <p>This is a helper interface to map the native type of the expected value to other result types.
+ *
+ * @since 1.1
+ */
+@FunctionalInterface
+interface IntStatisticResult extends StatisticResult {
+
+ @Override
+ int getAsInt();
+
+ @Override
+ default double getAsDouble() {
+ return getAsInt();
+ }
+
+ @Override
+ default long getAsLong() {
+ return getAsInt();
+ }
+
+ @Override
+ default BigInteger getAsBigInteger() {
+ return BigInteger.valueOf(getAsInt());
+ }
+}
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMaxTest.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMaxTest.java
new file mode 100644
index 0000000..f7c239e
--- /dev/null
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMaxTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.statistics.descriptive;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+import org.apache.commons.statistics.distribution.DoubleTolerance;
+import org.apache.commons.statistics.distribution.DoubleTolerances;
+
+/**
+ * Test for {@link LongMax}.
+ */
+final class LongMaxTest extends BaseLongStatisticTest<LongMax> {
+
+ @Override
+ protected ResultType getResultType() {
+ return ResultType.LONG;
+ }
+
+ @Override
+ protected LongMax create() {
+ return LongMax.create();
+ }
+
+ @Override
+ protected LongMax create(long... values) {
+ return LongMax.of(values);
+ }
+
+ @Override
+ protected DoubleStatistic createAsDoubleStatistic(long... values) {
+ return Max.of(Arrays.stream(values).asDoubleStream().toArray());
+ }
+
+ @Override
+ protected DoubleTolerance getToleranceAsDouble() {
+ return DoubleTolerances.equals();
+ }
+
+ @Override
+ protected StatisticResult getEmptyValue() {
+ return createStatisticResult(Long.MIN_VALUE);
+ }
+
+ @Override
+ protected StatisticResult getExpectedValue(long[] values) {
+ // Use the JDK as a reference implementation
+ final long x = Arrays.stream(values).max().orElse(Long.MIN_VALUE);
+ return createStatisticResult(x);
+ }
+
+ @Override
+ protected Stream<StatisticTestData> streamTestData() {
+ return Stream.of(
+ addCase(Long.MAX_VALUE - 1),
+ addCase(Long.MIN_VALUE + 1)
+ );
+ }
+}
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMinTest.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMinTest.java
new file mode 100644
index 0000000..1896e03
--- /dev/null
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMinTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.statistics.descriptive;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+import org.apache.commons.statistics.distribution.DoubleTolerance;
+import org.apache.commons.statistics.distribution.DoubleTolerances;
+
+/**
+ * Test for {@link LongMin}.
+ */
+final class LongMinTest extends BaseLongStatisticTest<LongMin> {
+
+ @Override
+ protected ResultType getResultType() {
+ return ResultType.LONG;
+ }
+
+ @Override
+ protected LongMin create() {
+ return LongMin.create();
+ }
+
+ @Override
+ protected LongMin create(long... values) {
+ return LongMin.of(values);
+ }
+
+ @Override
+ protected DoubleStatistic createAsDoubleStatistic(long... values) {
+ return Min.of(Arrays.stream(values).asDoubleStream().toArray());
+ }
+
+ @Override
+ protected DoubleTolerance getToleranceAsDouble() {
+ return DoubleTolerances.equals();
+ }
+
+ @Override
+ protected StatisticResult getEmptyValue() {
+ return createStatisticResult(Long.MAX_VALUE);
+ }
+
+ @Override
+ protected StatisticResult getExpectedValue(long[] values) {
+ // Use the JDK as a reference implementation
+ final long x = Arrays.stream(values).min().orElse(Long.MAX_VALUE);
+ return createStatisticResult(x);
+ }
+
+ @Override
+ protected Stream<StatisticTestData> streamTestData() {
+ return Stream.of(
+ addCase(Long.MAX_VALUE - 1),
+ addCase(Long.MIN_VALUE + 1)
+ );
+ }
+}
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongStatisticResult.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongStatisticResult.java
new file mode 100644
index 0000000..54f8369
--- /dev/null
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongStatisticResult.java
@@ -0,0 +1,48 @@
+/*
+ * 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.statistics.descriptive;
+
+import java.math.BigInteger;
+
+/**
+ * Represents the {@code long} result of a statistic computed over a set of values.
+ *
+ * <p>This is a helper interface to map the native type of the expected value to other result types.
+ *
+ * @since 1.1
+ */
+@FunctionalInterface
+interface LongStatisticResult extends StatisticResult {
+
+ @Override
+ long getAsLong();
+
+ @Override
+ default double getAsDouble() {
+ return getAsLong();
+ }
+
+ @Override
+ default int getAsInt() {
+ return Math.toIntExact(getAsLong());
+ }
+
+ @Override
+ default BigInteger getAsBigInteger() {
+ return BigInteger.valueOf(getAsLong());
+ }
+}
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/ResultType.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/ResultType.java
new file mode 100644
index 0000000..e30045e
--- /dev/null
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/ResultType.java
@@ -0,0 +1,31 @@
+/*
+ * 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.statistics.descriptive;
+
+/**
+ * The native result type of a {@link StatisticResult}.
+ */
+enum ResultType {
+ /** {@code double}. */
+ DOUBLE,
+ /** {@code int}. */
+ INT,
+ /** {@code long}. */
+ LONG,
+ /** {@code BigInteger}. */
+ BIG_INTEGER
+}
diff --git a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java
index ffebb45..2eb35e9 100644
--- a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java
+++ b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java
@@ -17,12 +17,15 @@
package org.apache.commons.statistics.descriptive;
import java.math.BigDecimal;
+import java.math.BigInteger;
import java.math.MathContext;
import java.util.Arrays;
import java.util.function.Supplier;
import org.apache.commons.rng.UniformRandomProvider;
import org.apache.commons.rng.simple.RandomSource;
import org.apache.commons.statistics.distribution.DoubleTolerance;
+import org.apache.commons.statistics.distribution.TestUtils;
+import org.junit.jupiter.api.Assertions;
/**
* Helper class for tests in {@code o.a.c.s.descriptive} module.
@@ -72,6 +75,30 @@ final class TestHelper {
.toArray();
}
+ /**
+ * Helper function to concatenate arrays.
+ *
+ * @param arrays Arrays to be concatenated.
+ * @return A new array containing elements from all input arrays in the order they appear.
+ */
+ static int[] concatenate(int[]... arrays) {
+ return Arrays.stream(arrays)
+ .flatMapToInt(Arrays::stream)
+ .toArray();
+ }
+
+ /**
+ * Helper function to concatenate arrays.
+ *
+ * @param arrays Arrays to be concatenated.
+ * @return A new array containing elements from all input arrays in the order they appear.
+ */
+ static long[] concatenate(long[]... arrays) {
+ return Arrays.stream(arrays)
+ .flatMapToLong(Arrays::stream)
+ .toArray();
+ }
+
/**
* Helper function to reverse the concatenation of arrays. The array is copied
* sequentially into the destination arrays.
@@ -90,6 +117,42 @@ final class TestHelper {
}
}
+ /**
+ * Helper function to reverse the concatenation of arrays. The array is copied
+ * sequentially into the destination arrays.
+ *
+ * <p>Warning: No lengths checks are performed. It is assume the combine length
+ * of the destination arrays is equal to the length of the data.
+ *
+ * @param data Array to be unconcatenated.
+ * @param arrays Destination arrays.
+ */
+ static void unconcatenate(int[] data, int[]... arrays) {
+ int from = 0;
+ for (final int[] a : arrays) {
+ System.arraycopy(data, from, a, 0, a.length);
+ from += a.length;
+ }
+ }
+
+ /**
+ * Helper function to reverse the concatenation of arrays. The array is copied
+ * sequentially into the destination arrays.
+ *
+ * <p>Warning: No lengths checks are performed. It is assume the combine length
+ * of the destination arrays is equal to the length of the data.
+ *
+ * @param data Array to be unconcatenated.
+ * @param arrays Destination arrays.
+ */
+ static void unconcatenate(long[] data, long[]... arrays) {
+ int from = 0;
+ for (final long[] a : arrays) {
+ System.arraycopy(data, from, a, 0, a.length);
+ from += a.length;
+ }
+ }
+
/**
* Helper function to compute the expected value of Mean using BigDecimal.
* @param values Values.
@@ -196,6 +259,46 @@ final class TestHelper {
return array;
}
+ /**
+ * Shuffles the entries of the given array.
+ *
+ * <p>Uses Fisher-Yates shuffle copied from
+ * <a href="https://github.com/apache/commons-rng/blob/master/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/ArraySampler.java">
+ * RNG ArraySampler.</a>
+ *
+ * <p>TODO: This can be removed when {@code commons-rng-sampling 1.6} is released.
+ *
+ * @param rng Source of randomness.
+ * @param array Array whose entries will be shuffled (in-place).
+ * @return Shuffled input array.
+ */
+ static int[] shuffle(UniformRandomProvider rng, int[] array) {
+ for (int i = array.length; i > 1; i--) {
+ swap(array, i - 1, rng.nextInt(i));
+ }
+ return array;
+ }
+
+ /**
+ * Shuffles the entries of the given array.
+ *
+ * <p>Uses Fisher-Yates shuffle copied from
+ * <a href="https://github.com/apache/commons-rng/blob/master/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/ArraySampler.java">
+ * RNG ArraySampler.</a>
+ *
+ * <p>TODO: This can be removed when {@code commons-rng-sampling 1.6} is released.
+ *
+ * @param rng Source of randomness.
+ * @param array Array whose entries will be shuffled (in-place).
+ * @return Shuffled input array.
+ */
+ static long[] shuffle(UniformRandomProvider rng, long[] array) {
+ for (int i = array.length; i > 1; i--) {
+ swap(array, i - 1, rng.nextInt(i));
+ }
+ return array;
+ }
+
/**
* Shuffles the entries of the given array.
*
@@ -229,6 +332,31 @@ final class TestHelper {
array[j] = tmp;
}
+ /**
+ * Swaps the two specified elements in the array.
+ *
+ * @param array Array.
+ * @param i First index.
+ * @param j Second index.
+ */
+ private static void swap(int[] array, int i, int j) {
+ final int tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
+
+ /**
+ * Swaps the two specified elements in the array.
+ *
+ * @param array Array.
+ * @param i First index.
+ * @param j Second index.
+ */
+ private static void swap(long[] array, int i, int j) {
+ final long tmp = array[i];
+ array[i] = array[j];
+ array[j] = tmp;
+ }
/**
* Swaps the two specified elements in the array.
*
@@ -241,4 +369,82 @@ final class TestHelper {
array[i] = array[j];
array[j] = tmp;
}
+
+ /**
+ * Assert the results are equal. All result types are compared.
+ *
+ * <p>If the tolerance is null then it is assumed the result is an exact integer type;
+ * the double value is compared using binary equality.
+ *
+ * <p>If the tolerance is not null then it is assumed the result is a {@code double} type;
+ * the BigInteger value is compared using its {@code double} representation if the
+ * actual BigInteger result is not equal.
+ *
+ * <p>If the {@code expected} throws an exception, then the {@code actual} is asserted
+ * to throw an exception of the same class.
+ *
+ * @param expected Expected.
+ * @param actual Actual.
+ * @param tol Tolerance for double equality.
+ * @param msg Message used for failure.
+ */
+ static void assertEquals(StatisticResult expected, StatisticResult actual, DoubleTolerance tol,
+ Supplier<String> msg) {
+ final Supplier<String> intMsg = () -> prefix(msg) + " int value";
+ Integer i = null;
+ try {
+ i = expected.getAsInt();
+ } catch (Throwable t) {
+ Assertions.assertThrowsExactly(t.getClass(), () -> actual.getAsInt(), intMsg);
+ }
+ if (i != null) {
+ Assertions.assertEquals(i.intValue(), actual.getAsInt(), intMsg);
+ }
+
+ final Supplier<String> longMsg = () -> prefix(msg) + " long value";
+ Long l = null;
+ try {
+ l = expected.getAsLong();
+ } catch (Throwable t) {
+ Assertions.assertThrowsExactly(t.getClass(), () -> actual.getAsLong(), longMsg);
+ }
+ if (l != null) {
+ Assertions.assertEquals(l.longValue(), actual.getAsLong(), longMsg);
+ }
+
+ final Supplier<String> doubleMsg = () -> prefix(msg) + " double value";
+ Double d = null;
+ try {
+ d = expected.getAsDouble();
+ } catch (Throwable t) {
+ Assertions.assertThrowsExactly(t.getClass(), () -> actual.getAsDouble(), doubleMsg);
+ }
+ if (d != null) {
+ if (tol == null) {
+ Assertions.assertEquals(d, actual.getAsDouble(), doubleMsg);
+ } else {
+ TestUtils.assertEquals(d, actual.getAsDouble(), tol, doubleMsg);
+ }
+ }
+
+ final Supplier<String> bigMsg = () -> prefix(msg) + " BigInteger value";
+ BigInteger b = null;
+ try {
+ b = expected.getAsBigInteger();
+ } catch (Throwable t) {
+ Assertions.assertThrowsExactly(t.getClass(), () -> actual.getAsBigInteger(), bigMsg);
+ }
+ if (b != null) {
+ if (tol == null) {
+ // Assume exact
+ Assertions.assertEquals(b, actual.getAsBigInteger(), bigMsg);
+ } else {
+ // Double computation may not be exact so check within tolerance
+ final BigInteger bb = actual.getAsBigInteger();
+ if (!b.equals(bb)) {
+ TestUtils.assertEquals(b.doubleValue(), bb.doubleValue(), tol, bigMsg);
+ }
+ }
+ }
+ }
}
diff --git a/src/conf/checkstyle/checkstyle-suppressions.xml b/src/conf/checkstyle/checkstyle-suppressions.xml
index 7086dac..8216ef6 100644
--- a/src/conf/checkstyle/checkstyle-suppressions.xml
+++ b/src/conf/checkstyle/checkstyle-suppressions.xml
@@ -39,4 +39,6 @@
<suppress checks="ParameterNumber" files=".*[/\\]WilcoxonSignedRankTestTest.java" />
<suppress checks="ParameterNumber" files=".*[/\\]UnconditionedExactTestTest.java" />
<suppress checks="MethodLength" files=".*[/\\]WilcoxonSignedRankTestTest.java" />
+ <suppress checks="IllegalCatch" files=".*[/\\]TestHelper.java" lines="390-450" />
+ <suppress checks="IllegalCatch" files=".*[/\\]BaseStatisticTest.java" lines="280-400" />
</suppressions>