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
The following commit(s) were added to refs/heads/master by this push:
new 171b29a STATISTICS-81: Add integer sum implementation
171b29a is described below
commit 171b29a5780ccbbbf06ff3a08a5e63ec5dedd673
Author: Alex Herbert <[email protected]>
AuthorDate: Sat Dec 23 16:26:42 2023 +0000
STATISTICS-81: Add integer sum implementation
---
.../commons/statistics/descriptive/Int128.java | 62 +++++++
.../commons/statistics/descriptive/IntMath.java | 4 +-
.../commons/statistics/descriptive/IntSum.java | 185 +++++++++++++++++++++
.../commons/statistics/descriptive/LongSum.java | 180 ++++++++++++++++++++
.../commons/statistics/descriptive/UInt128.java | 2 +-
.../commons/statistics/descriptive/UInt192.java | 4 +-
.../commons/statistics/descriptive/Int128Test.java | 27 +++
.../statistics/descriptive/IntMathTest.java | 6 +-
.../statistics/descriptive/IntMeanTest.java | 2 +-
.../{IntMeanTest.java => IntSumTest.java} | 67 ++++----
.../statistics/descriptive/LongMeanTest.java | 2 +-
.../{LongMeanTest.java => LongSumTest.java} | 74 ++++-----
12 files changed, 522 insertions(+), 93 deletions(-)
diff --git
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Int128.java
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Int128.java
index 5ae6357..bf0b99e 100644
---
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Int128.java
+++
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Int128.java
@@ -195,6 +195,44 @@ final class Int128 {
.putLong(h).putLong(l).array());
}
+ /**
+ * Convert to a {@code double}.
+ *
+ * @return the value
+ */
+ double toDouble() {
+ long h = hi;
+ long l = lo;
+ // Special cases
+ if (h == 0) {
+ return l;
+ }
+ if (l == 0) {
+ return h * 0x1.0p64;
+ }
+ // Both h and l are non-zero and can be negated to a positive
magnitude.
+ // Use the same logic as toBigInteger to create magnitude (h, l) and a
sign.
+ int sign = 1;
+ if ((h ^ l) < 0) {
+ // Here we rearrange to [2^64 * (hi64-1)] + [2^64 - lo64].
+ if (h >= 0) {
+ h = h - 1;
+ } else {
+ // As above with negation
+ h = ~h; // -h - 1
+ l = -l;
+ sign = -1;
+ }
+ } else if (h < 0) {
+ // Invert negative values to create the equivalent positive
magnitude.
+ h = -h;
+ l = -l;
+ sign = -1;
+ }
+ final double x = IntMath.uint128ToDouble(h, l);
+ return sign < 0 ? -x : x;
+ }
+
/**
* Convert to a double-double.
*
@@ -211,6 +249,30 @@ final class Int128 {
return DD.of(lo).add((hi & MASK32) * 0x1.0p64).add((hi >>
Integer.SIZE) * 0x1.0p96);
}
+ /**
+ * Convert to an {@code int}; throwing an exception if the value overflows
an {@code int}.
+ *
+ * @return the value
+ * @throws ArithmeticException if the value overflows an {@code int}.
+ * @see Math#toIntExact(long)
+ */
+ int toIntExact() {
+ return Math.toIntExact(toLongExact());
+ }
+
+ /**
+ * Convert to a {@code long}; throwing an exception if the value overflows
a {@code long}.
+ *
+ * @return the value
+ * @throws ArithmeticException if the value overflows a {@code long}.
+ */
+ long toLongExact() {
+ if (hi != 0) {
+ throw new ArithmeticException("long integer overflow");
+ }
+ return lo;
+ }
+
/**
* Return the lower 64-bits as a {@code long} value.
*
diff --git
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMath.java
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMath.java
index 714fc41..2309750 100644
---
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMath.java
+++
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMath.java
@@ -212,7 +212,7 @@ final class IntMath {
// Implicit conversion to a double.
return lo;
}
- return uin128ToDouble(unsignedMultiplyHigh(x, y), lo);
+ return uint128ToDouble(unsignedMultiplyHigh(x, y), lo);
}
/**
@@ -222,7 +222,7 @@ final class IntMath {
* @param lo Low 64-bits.
* @return the double
*/
- static double uin128ToDouble(long hi, long lo) {
+ static double uint128ToDouble(long hi, long lo) {
// Require the representation:
// 2^exp * mantissa / 2^53
// The mantissa has an implied leading 1-bit.
diff --git
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntSum.java
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntSum.java
new file mode 100644
index 0000000..d1f4775
--- /dev/null
+++
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntSum.java
@@ -0,0 +1,185 @@
+/*
+ * 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;
+
+/**
+ * Returns the sum of the available values.
+ *
+ * <ul>
+ * <li>The result is zero if no values are added.
+ * </ul>
+ *
+ * <p>This class uses an exact integer sum. It supports up to 2<sup>63</sup>
+ * values as the count \( n \) is maintained as a {@code long}. The exact sum
is
+ * returned using {@link #getAsBigInteger()}. Methods that return {@code int}
or
+ * {@code long} primitives will raise an exception if the result overflows.
+ * The {@code long} value is safe up to the maximum array length for any input
+ * {@code int[]} data. The {@code long} value can overflow when instances are
combined.
+ *
+ * <p>Note that the implementation does not use {@code BigInteger} arithmetic;
for
+ * performance the sum is computed using primitives to create a signed 128-bit
integer.
+ *
+ * <p>This class is designed to work with (though does not require)
+ * {@linkplain java.util.stream streams}.
+ *
+ * <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.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.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()}
+ * provides the necessary partitioning, isolation, and merging of results for
+ * safe and efficient parallel execution.
+ *
+ * @since 1.1
+ */
+public final class IntSum implements IntStatistic,
StatisticAccumulator<IntSum> {
+ /** Sum of the values. */
+ private final Int128 sum;
+
+ /**
+ * Create an instance.
+ */
+ private IntSum() {
+ this(Int128.create());
+ }
+
+ /**
+ * Create an instance.
+ *
+ * @param sum Sum of the values.
+ */
+ private IntSum(Int128 sum) {
+ this.sum = sum;
+ }
+
+ /**
+ * Creates an instance.
+ *
+ * <p>The initial result is zero.
+ *
+ * @return {@code IntSum} instance.
+ */
+ public static IntSum create() {
+ return new IntSum();
+ }
+
+ /**
+ * Returns an instance populated using the input {@code values}.
+ *
+ * <p>When the input is an empty array, the result is zero.
+ *
+ * <p>The {@link #getAsLong()} result is valid for any input {@code int[]}
length;
+ * the {@link #getAsInt()} result may overflow.
+ *
+ * @param values Values.
+ * @return {@code IntSum} instance.
+ */
+ public static IntSum of(int... values) {
+ // Sum of an array cannot exceed a 64-bit long
+ long s = 0;
+ for (final int x : values) {
+ s += x;
+ }
+ // Convert
+ return new IntSum(Int128.of(s));
+ }
+
+ /**
+ * Updates the state of the statistic to reflect the addition of {@code
value}.
+ *
+ * @param value Value.
+ */
+ @Override
+ public void accept(int value) {
+ sum.add(value);
+ }
+
+ /**
+ * Gets the sum of all input values.
+ *
+ * <p>When no values have been added, the result is zero.
+ *
+ * <p>Warning: This will raise an {@link ArithmeticException}
+ * if the result is not within the range {@code [-2^31, 2^31)}.
+ *
+ * @return sum of all values.
+ * @throws ArithmeticException if the {@code result} overflows an {@code
int}
+ * @see #getAsBigInteger()
+ */
+ @Override
+ public int getAsInt() {
+ return sum.toIntExact();
+ }
+
+ /**
+ * Gets the sum of all input values.
+ *
+ * <p>When no values have been added, the result is zero.
+ *
+ * <p>Warning: This will raise an {@link ArithmeticException}
+ * if the result is not within the range {@code [-2^63, 2^63)}.
+ *
+ * @return sum of all values.
+ * @throws ArithmeticException if the {@code result} overflows a {@code
long}
+ * @see #getAsBigInteger()
+ */
+ @Override
+ public long getAsLong() {
+ return sum.toLongExact();
+ }
+
+ /**
+ * Gets the sum of all input values.
+ *
+ * <p>When no values have been added, the result is zero.
+ *
+ * <p>Note that this conversion can lose information about the precision
of the
+ * {@code BigInteger} value.
+ *
+ * @return sum of all values.
+ * @see #getAsBigInteger()
+ */
+ @Override
+ public double getAsDouble() {
+ return sum.toDouble();
+ }
+
+ /**
+ * Gets the sum of all input values.
+ *
+ * <p>When no values have been added, the result is zero.
+ *
+ * @return sum of all values.
+ */
+ @Override
+ public BigInteger getAsBigInteger() {
+ return sum.toBigInteger();
+ }
+
+ @Override
+ public IntSum combine(IntSum other) {
+ sum.add(other.sum);
+ return this;
+ }
+}
diff --git
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongSum.java
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongSum.java
new file mode 100644
index 0000000..61efaa8
--- /dev/null
+++
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongSum.java
@@ -0,0 +1,180 @@
+/*
+ * 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;
+
+/**
+ * Returns the sum of the available values.
+ *
+ * <ul>
+ * <li>The result is zero if no values are added.
+ * </ul>
+ *
+ * <p>This class uses an exact integer sum. It supports up to 2<sup>63</sup>
+ * values as the count \( n \) is maintained as a {@code long}. The exact sum
is
+ * returned using {@link #getAsBigInteger()}. Methods that return {@code int}
or
+ * {@code long} primitives will raise an exception if the result overflows.
+ * The {@code long} value is safe up to the maximum array length for any input
+ * {@code int[]} data. The {@code long} value can overflow when instances are
combined.
+ *
+ * <p>Note that the implementation does not use {@code BigInteger} arithmetic;
for
+ * performance the sum is computed using primitives to create a signed 128-bit
integer.
+ *
+ * <p>This class is designed to work with (though does not require)
+ * {@linkplain java.util.stream streams}.
+ *
+ * <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.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.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()}
+ * provides the necessary partitioning, isolation, and merging of results for
+ * safe and efficient parallel execution.
+ *
+ * @since 1.1
+ */
+public final class LongSum implements LongStatistic,
StatisticAccumulator<LongSum> {
+ /** Sum of the values. */
+ private final Int128 sum;
+
+ /**
+ * Create an instance.
+ */
+ private LongSum() {
+ this(Int128.create());
+ }
+
+ /**
+ * Create an instance.
+ *
+ * @param sum Sum of the values.
+ */
+ private LongSum(Int128 sum) {
+ this.sum = sum;
+ }
+
+ /**
+ * Creates an instance.
+ *
+ * <p>The initial result is zero.
+ *
+ * @return {@code IntSum} instance.
+ */
+ public static LongSum create() {
+ return new LongSum();
+ }
+
+ /**
+ * Returns an instance populated using the input {@code values}.
+ *
+ * <p>When the input is an empty array, the result is zero.
+ *
+ * @param values Values.
+ * @return {@code IntSum} instance.
+ */
+ public static LongSum of(long... values) {
+ final Int128 s = Int128.create();
+ for (final long x : values) {
+ s.add(x);
+ }
+ return new LongSum(s);
+ }
+
+ /**
+ * Updates the state of the statistic to reflect the addition of {@code
value}.
+ *
+ * @param value Value.
+ */
+ @Override
+ public void accept(long value) {
+ sum.add(value);
+ }
+
+ /**
+ * Gets the sum of all input values.
+ *
+ * <p>When no values have been added, the result is zero.
+ *
+ * <p>Warning: This will raise an {@link ArithmeticException}
+ * if the result is not within the range {@code [-2^31, 2^31)}.
+ *
+ * @return sum of all values.
+ * @throws ArithmeticException if the {@code result} overflows an {@code
int}
+ * @see #getAsBigInteger()
+ */
+ @Override
+ public int getAsInt() {
+ return sum.toIntExact();
+ }
+
+ /**
+ * Gets the sum of all input values.
+ *
+ * <p>When no values have been added, the result is zero.
+ *
+ * <p>Warning: This will raise an {@link ArithmeticException}
+ * if the result is not within the range {@code [-2^63, 2^63)}.
+ *
+ * @return sum of all values.
+ * @throws ArithmeticException if the {@code result} overflows a {@code
long}
+ * @see #getAsBigInteger()
+ */
+ @Override
+ public long getAsLong() {
+ return sum.toLongExact();
+ }
+
+ /**
+ * Gets the sum of all input values.
+ *
+ * <p>When no values have been added, the result is zero.
+ *
+ * <p>Note that this conversion can lose information about the precision
of the
+ * {@code BigInteger} value.
+ *
+ * @return sum of all values.
+ * @see #getAsBigInteger()
+ */
+ @Override
+ public double getAsDouble() {
+ return sum.toDouble();
+ }
+
+ /**
+ * Gets the sum of all input values.
+ *
+ * <p>When no values have been added, the result is zero.
+ *
+ * @return sum of all values.
+ */
+ @Override
+ public BigInteger getAsBigInteger() {
+ return sum.toBigInteger();
+ }
+
+ @Override
+ public LongSum combine(LongSum other) {
+ sum.add(other.sum);
+ return this;
+ }
+}
diff --git
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/UInt128.java
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/UInt128.java
index c4b421c..1d12a1e 100644
---
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/UInt128.java
+++
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/UInt128.java
@@ -197,7 +197,7 @@ final class UInt128 {
* @return the value
*/
double toDouble() {
- return IntMath.uin128ToDouble(hi64(), lo64());
+ return IntMath.uint128ToDouble(hi64(), lo64());
}
/**
diff --git
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/UInt192.java
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/UInt192.java
index f390944..df46f84 100644
---
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/UInt192.java
+++
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/UInt192.java
@@ -211,11 +211,11 @@ final class UInt192 {
final long m = mid64();
final long l = lo64();
if (h == 0) {
- return IntMath.uin128ToDouble(m, l);
+ return IntMath.uint128ToDouble(m, l);
}
// For correct rounding we use a sticky bit to represent magnitude
// lost from the low 64-bits. The result is scaled by 2^64.
- return IntMath.uin128ToDouble(h, m | ((l == 0) ? 0 : 1)) * 0x1.0p64;
+ return IntMath.uint128ToDouble(h, m | ((l == 0) ? 0 : 1)) * 0x1.0p64;
}
/**
diff --git
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/Int128Test.java
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/Int128Test.java
index 042ef10..7a5f298 100644
---
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/Int128Test.java
+++
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/Int128Test.java
@@ -29,6 +29,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
/**
* Test for {@link Int128}.
@@ -86,6 +87,7 @@ class Int128Test {
}
Assertions.assertEquals(expected, v.toBigInteger());
// Check floating-point representation
+ Assertions.assertEquals(expected.doubleValue(), v.toDouble(),
"double");
TestHelper.assertEquals(new BigDecimal(expected), v.toDD(),
0x1.0p-106, "DD");
}
@@ -124,6 +126,7 @@ class Int128Test {
Assertions.assertEquals(expected, x.toBigInteger(),
() -> String.format("(%d, %d) + (%d, %d)", a, b, c, d));
// Check floating-point representation
+ Assertions.assertEquals(expected.doubleValue(), x.toDouble(),
"double");
TestHelper.assertEquals(new BigDecimal(expected), x.toDD(),
0x1.0p-106, "DD");
// Check self-addition
expected = y.toBigInteger();
@@ -170,4 +173,28 @@ class Int128Test {
RandomSource.XO_RO_SHI_RO_128_PP.create().longs(20).forEach(builder);
return builder.build();
}
+
+ @ParameterizedTest
+ @ValueSource(ints = {Integer.MAX_VALUE, Integer.MIN_VALUE})
+ void testToIntExact(int x) {
+ final Int128 v = Int128.of(x);
+ Assertions.assertEquals(x, v.toIntExact());
+ final int y = x < 0 ? -1 : 1;
+ v.add(y);
+ Assertions.assertThrows(ArithmeticException.class, () ->
v.toIntExact());
+ v.add(-y);
+ Assertions.assertEquals(x, v.toIntExact());
+ }
+
+ @ParameterizedTest
+ @ValueSource(longs = {Long.MAX_VALUE, Long.MIN_VALUE})
+ void testToLongExact(long x) {
+ final Int128 v = Int128.of(x);
+ Assertions.assertEquals(x, v.toLongExact());
+ final int y = x < 0 ? -1 : 1;
+ v.add(y);
+ Assertions.assertThrows(ArithmeticException.class, () ->
v.toLongExact());
+ v.add(-y);
+ Assertions.assertEquals(x, v.toLongExact());
+ }
}
diff --git
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMathTest.java
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMathTest.java
index 09721ff..c5785c0 100644
---
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMathTest.java
+++
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMathTest.java
@@ -113,15 +113,15 @@ class IntMathTest {
@ParameterizedTest
@MethodSource
- void testUin128ToDouble(long a, long b) {
+ void testUint128ToDouble(long a, long b) {
final BigInteger bi1 = toUnsignedBigInteger(a).shiftLeft(Long.SIZE);
final BigInteger bi2 = toUnsignedBigInteger(b);
final double x = bi1.add(bi2).doubleValue();
- Assertions.assertEquals(x, IntMath.uin128ToDouble(a, b),
+ Assertions.assertEquals(x, IntMath.uint128ToDouble(a, b),
() -> String.format("%s + %s", a, b));
}
- static Stream<Arguments> testUin128ToDouble() {
+ static Stream<Arguments> testUint128ToDouble() {
final UniformRandomProvider rng =
RandomSource.XO_RO_SHI_RO_128_PP.create();
final Stream.Builder<Arguments> builder = Stream.builder();
for (int i = 0; i < 100; i++) {
diff --git
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMeanTest.java
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMeanTest.java
index 60d5449..5dde438 100644
---
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMeanTest.java
+++
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMeanTest.java
@@ -90,7 +90,7 @@ final class IntMeanTest extends BaseIntStatisticTest<IntMean>
{
}
/**
- * Test a large integer sums that overflow a {@code long}.
+ * Test large integer sums that overflow a {@code long}.
* Overflow is created by repeat addition.
*
* <p>Note: Currently no check is made for overflow in the
diff --git
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMeanTest.java
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntSumTest.java
similarity index 57%
copy from
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMeanTest.java
copy to
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntSumTest.java
index 60d5449..10c90dd 100644
---
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMeanTest.java
+++
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntSumTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.commons.statistics.descriptive;
+import java.math.BigInteger;
import java.util.Arrays;
import java.util.stream.Stream;
import org.apache.commons.statistics.distribution.DoubleTolerance;
@@ -25,72 +26,61 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
/**
- * Test for {@link IntMean}.
+ * Test for {@link IntSum}.
*/
-final class IntMeanTest extends BaseIntStatisticTest<IntMean> {
+final class IntSumTest extends BaseIntStatisticTest<IntSum> {
@Override
- protected IntMean create() {
- return IntMean.create();
+ protected ResultType getResultType() {
+ return ResultType.BIG_INTEGER;
}
@Override
- protected IntMean create(int... values) {
- return IntMean.of(values);
+ protected IntSum create() {
+ return IntSum.create();
+ }
+
+ @Override
+ protected IntSum create(int... values) {
+ return IntSum.of(values);
}
@Override
protected DoubleStatistic createAsDoubleStatistic(int... values) {
- return Mean.of(Arrays.stream(values).asDoubleStream().toArray());
+ return Sum.of(Arrays.stream(values).asDoubleStream().toArray());
}
@Override
protected DoubleTolerance getToleranceAsDouble() {
- // Large shifts in the rolling mean are not computed very accurately
- return DoubleTolerances.relative(5e-8);
+ // Floating-point sum may be inexact.
+ // Currently the double sum matches on the standard test data.
+ return DoubleTolerances.equals();
}
@Override
protected StatisticResult getEmptyValue() {
- return createStatisticResult(Double.NaN);
+ // It does not matter that this returns a IntStatisticResult
+ // rather than a BigIntegerStatisticResult
+ return createStatisticResult(0);
}
@Override
protected StatisticResult getExpectedValue(int[] values) {
// Use the JDK as a reference implementation
- final double x = Arrays.stream(values).average().orElse(Double.NaN);
+ final long x = Arrays.stream(values).asLongStream().sum();
return createStatisticResult(x);
}
- @Override
- protected DoubleTolerance getTolerance() {
- return DoubleTolerances.equals();
- }
-
@Override
protected Stream<StatisticTestData> streamTestData() {
- final Stream.Builder<StatisticTestData> builder = Stream.builder();
- builder.accept(addCase(Integer.MAX_VALUE - 1, Integer.MAX_VALUE));
- builder.accept(addCase(Integer.MIN_VALUE + 1, Integer.MIN_VALUE));
- final int[] a = new int[2 * 512 * 512];
- Arrays.fill(a, 0, a.length / 2, 10);
- Arrays.fill(a, a.length / 2, a.length, 1);
- builder.accept(addReference(5.5, a));
-
- // Same cases as for the DoubleStatistic Variance but the tolerance is
exact
- final DoubleTolerance tol = DoubleTolerances.equals();
-
- // Python Numpy v1.25.1: numpy.mean
- builder.accept(addReference(2.5, tol, 1, 2, 3, 4));
- builder.accept(addReference(12.0, tol, 5, 9, 13, 14, 10, 12, 11, 15,
19));
- // R v4.3.1: mean(x)
- builder.accept(addReference(5.5, tol, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
- builder.accept(addReference(8.75, tol, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 50));
- return builder.build();
+ return Stream.of(
+ addCase(Integer.MAX_VALUE, 1, 2, 3, 4, Integer.MAX_VALUE),
+ addCase(Integer.MIN_VALUE, -1, -2, -3, -4, Integer.MIN_VALUE)
+ );
}
/**
- * Test a large integer sums that overflow a {@code long}.
+ * Test large integer sums that overflow a {@code long}.
* Overflow is created by repeat addition.
*
* <p>Note: Currently no check is made for overflow in the
@@ -105,12 +95,13 @@ final class IntMeanTest extends
BaseIntStatisticTest<IntMean> {
"-2147483648, -2147483647, 61",
})
void testLongOverflow(int x, int y, int exp) {
- final IntMean s = IntMean.of(x, y);
- final double mean = ((long) x + y) * 0.5;
+ final IntSum s = IntSum.of(x, y);
+ BigInteger sum = BigInteger.valueOf((long) x + y);
for (int i = 0; i < exp; i++) {
// Assumes the sum as a long will overflow
s.combine(s);
- Assertions.assertEquals(mean, s.getAsDouble());
+ sum = sum.shiftLeft(1);
+ Assertions.assertEquals(sum, s.getAsBigInteger());
}
}
}
diff --git
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMeanTest.java
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMeanTest.java
index 0d0f86b..849732e 100644
---
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMeanTest.java
+++
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMeanTest.java
@@ -97,7 +97,7 @@ final class LongMeanTest extends
BaseLongStatisticTest<LongMean> {
}
/**
- * Test a large integer sums that overflow a {@code long}.
+ * Test large integer sums that overflow a {@code long}.
* Overflow is created by repeat addition.
*
* <p>Note: Currently no check is made for overflow in the
diff --git
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMeanTest.java
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongSumTest.java
similarity index 55%
copy from
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMeanTest.java
copy to
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongSumTest.java
index 0d0f86b..9207112 100644
---
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongMeanTest.java
+++
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/LongSumTest.java
@@ -16,9 +16,7 @@
*/
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.stream.Stream;
import org.apache.commons.statistics.distribution.DoubleTolerance;
@@ -28,76 +26,62 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
/**
- * Test for {@link LongMean}.
+ * Test for {@link LongSum}.
*/
-final class LongMeanTest extends BaseLongStatisticTest<LongMean> {
+final class LongSumTest extends BaseLongStatisticTest<LongSum> {
@Override
- protected LongMean create() {
- return LongMean.create();
+ protected ResultType getResultType() {
+ return ResultType.BIG_INTEGER;
}
@Override
- protected LongMean create(long... values) {
- return LongMean.of(values);
+ protected LongSum create() {
+ return LongSum.create();
}
@Override
- protected StatisticResult getEmptyValue() {
- return createStatisticResult(Double.NaN);
+ protected LongSum create(long... values) {
+ return LongSum.of(values);
}
@Override
protected DoubleStatistic createAsDoubleStatistic(long... values) {
- return Mean.of(Arrays.stream(values).asDoubleStream().toArray());
+ return Sum.of(Arrays.stream(values).asDoubleStream().toArray());
}
@Override
protected DoubleTolerance getToleranceAsDouble() {
- // Data with large shifts in the rolling mean is not computed very
accurately
- return DoubleTolerances.relative(5e-8);
+ // Floating-point sum may be inexact.
+ // Currently the double sum matches on the standard test data.
+ return DoubleTolerances.equals();
+ }
+
+ @Override
+ protected StatisticResult getEmptyValue() {
+ // It does not matter that this returns a IntStatisticResult
+ // rather than a BigIntegerStatisticResult
+ return createStatisticResult(0);
}
@Override
protected StatisticResult getExpectedValue(long[] values) {
- final BigInteger sum = Arrays.stream(values)
+ final BigInteger x = Arrays.stream(values)
.mapToObj(BigInteger::valueOf)
.reduce(BigInteger.ZERO, BigInteger::add);
- final double x = new BigDecimal(sum)
- .divide(BigDecimal.valueOf(values.length), MathContext.DECIMAL128)
- .doubleValue();
return createStatisticResult(x);
}
- @Override
- protected DoubleTolerance getTolerance() {
- return DoubleTolerances.equals();
- }
-
@Override
protected Stream<StatisticTestData> streamTestData() {
- final Stream.Builder<StatisticTestData> builder = Stream.builder();
- builder.accept(addCase(Long.MAX_VALUE - 1, Long.MAX_VALUE));
- builder.accept(addCase(Long.MIN_VALUE + 1, Long.MIN_VALUE));
- final long[] a = new long[2 * 512 * 512];
- Arrays.fill(a, 0, a.length / 2, 10);
- Arrays.fill(a, a.length / 2, a.length, 1);
- builder.accept(addReference(5.5, a));
-
- // Same cases as for the DoubleStatistic Variance but the tolerance is
exact
- final DoubleTolerance tol = DoubleTolerances.equals();
-
- // Python Numpy v1.25.1: numpy.mean
- builder.accept(addReference(2.5, tol, 1, 2, 3, 4));
- builder.accept(addReference(12.0, tol, 5, 9, 13, 14, 10, 12, 11, 15,
19));
- // R v4.3.1: mean(x)
- builder.accept(addReference(5.5, tol, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
- builder.accept(addReference(8.75, tol, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 50));
- return builder.build();
+ return Stream.of(
+ addCase(Long.MAX_VALUE, 1, 2, 3, 4, Long.MAX_VALUE),
+ addCase(Long.MIN_VALUE, -1, -2, -3, -4, Long.MIN_VALUE)
+ );
}
/**
- * Test a large integer sums that overflow a {@code long}.
+ * Test large integer sums that overflow a {@code long}.
* Overflow is created by repeat addition.
*
* <p>Note: Currently no check is made for overflow in the
@@ -112,13 +96,13 @@ final class LongMeanTest extends
BaseLongStatisticTest<LongMean> {
"-9223372036854775808, -9223372036854775807, 61",
})
void testLongOverflow(long x, long y, int exp) {
- final LongMean s = LongMean.of(x, y);
- final double mean = BigInteger.valueOf(x)
- .add(BigInteger.valueOf(y)).doubleValue() * 0.5;
+ final LongSum s = LongSum.of(x, y);
+ BigInteger sum = BigInteger.valueOf(x).add(BigInteger.valueOf(y));
for (int i = 0; i < exp; i++) {
// Assumes the sum as a long will overflow
s.combine(s);
- Assertions.assertEquals(mean, s.getAsDouble());
+ sum = sum.shiftLeft(1);
+ Assertions.assertEquals(sum, s.getAsBigInteger());
}
}
}