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 4633d9b  STATISTICS-76: Max Implementation
4633d9b is described below

commit 4633d9bb7989dc7b168ab04456b1a085d59d53b5
Author: AJoshi <janiru...@gmail.com>
AuthorDate: Sun Jul 16 23:34:56 2023 +0530

    STATISTICS-76: Max Implementation
---
 .../apache/commons/statistics/descriptive/Max.java | 134 +++++++++++++++
 .../commons/statistics/descriptive/MaxTest.java    | 188 +++++++++++++++++++++
 src/conf/pmd/pmd-ruleset.xml                       |   2 +-
 3 files changed, 323 insertions(+), 1 deletion(-)

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
new file mode 100644
index 0000000..82d625a
--- /dev/null
+++ 
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Max.java
@@ -0,0 +1,134 @@
+/*
+ * 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;
+
+/**
+ * Returns the maximum of the available values.
+ *
+ * <p>The result is <code>NaN</code> if any of the values is <code>NaN</code>.
+ *
+ * <p>The result is <code>NEGATIVE_INFINITY</code> if no values are added.
+ *
+ * <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 <code>accept()</code> or
+ * <code>combine()</code> method, it must be synchronized externally.
+ *
+ * <p>However, it is safe to use <code>accept()</code> and 
<code>combine()</code>
+ * as <code>accumulator</code> and <code>combiner</code> 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 abstract class Max implements DoubleStatistic, 
DoubleStatisticAccumulator<Max> {
+
+    /**
+     * Create a Max instance.
+     */
+    Max() {
+        //No-op
+    }
+
+    /**
+     * Creates a {@code Max} implementation which does not store the input 
value(s) it consumes.
+     *
+     * <p>The result is <code>NaN</code> if any of the values is 
<code>NaN</code>.
+     *
+     * <p>The result is {@link Double#NEGATIVE_INFINITY NEGATIVE_INFINITY}
+     * if no values have been added.
+     *
+     * @return {@code Max} implementation.
+     */
+    public static Max create() {
+        return new StorelessMax();
+    }
+
+    /**
+     * Returns a {@code Max} instance that has the maximum of all input 
value(s).
+     *
+     * <p>The result is <code>NaN</code> if any of the values is 
<code>NaN</code>.
+     *
+     * <p>When the input is an empty array, the result is
+     * {@link Double#NEGATIVE_INFINITY NEGATIVE_INFINITY}.
+     *
+     * @param values Values.
+     * @return {@code Max} instance.
+     */
+    public static Max of(double... values) {
+        final StorelessMax max = new StorelessMax();
+        Arrays.stream(values).forEach(max);
+        return max;
+    }
+
+    /**
+     * Updates the state of the statistic to reflect the addition of {@code 
value}.
+     * @param value Value.
+     */
+    @Override
+    public abstract void accept(double value);
+
+    /**
+     * Gets the maximum of all input values.
+     *
+     * <p>When no values have been added, the result is
+     * {@link Double#NEGATIVE_INFINITY NEGATIVE_INFINITY}.
+     *
+     * @return {@code Maximum} of all values seen so far.
+     */
+    @Override
+    public abstract double getAsDouble();
+
+    /** {@inheritDoc} */
+    @Override
+    public abstract Max combine(Max other);
+
+    /**
+     * {@code Max} implementation that does not store the input value(s) 
processed so far.
+     *
+     * <p>Uses JDK's {@link Math#max Math.max} as an underlying function
+     * to compute the {@code maximum}.
+     */
+    private static class StorelessMax extends Max {
+
+        /** Current max. */
+        private double max = Double.NEGATIVE_INFINITY;
+
+        @Override
+        public void accept(double value) {
+            max = Double.max(max, value);
+        }
+
+        @Override
+        public double getAsDouble() {
+            return max;
+        }
+
+        @Override
+        public Max combine(Max other) {
+            accept(other.getAsDouble());
+            return this;
+        }
+    }
+}
diff --git 
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MaxTest.java
 
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MaxTest.java
new file mode 100644
index 0000000..0051d1d
--- /dev/null
+++ 
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MaxTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.function.DoubleSupplier;
+import java.util.stream.Stream;
+import org.apache.commons.rng.UniformRandomProvider;
+import org.junit.jupiter.api.Assertions;
+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;
+
+/**
+ * Test for {@link Max}.
+ */
+final class MaxTest {
+
+    @Test
+    void testEmpty() {
+        Max max = Max.create();
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, max.getAsDouble());
+    }
+
+    @Test
+    void testIncrement() {
+        // Test the max after each incremental update
+        // First parameter of testArray is the value that would be added
+        // Second parameter of testArray is the max we expect after adding the 
value
+        double[][] testArray = {
+            {1729.22, 1729.22},
+            {2520.35, 2520.35},
+            {2010.87, 2520.35},
+            {100000000.1, 100000000.1},
+            {+0.0, 100000000.1},
+            {-0.0, 100000000.1},
+            {Double.MAX_VALUE, Double.MAX_VALUE},
+            {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
+            {Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY}
+        };
+
+        Max stat = Max.create();
+        for (final double[] valueAndExpected: testArray) {
+            final double value = valueAndExpected[0];
+            final double expected = valueAndExpected[1];
+            stat.accept(value);
+            Assertions.assertEquals(expected, stat.getAsDouble());
+        }
+    }
+
+    @Test
+    void testNaN() {
+        // Test non-nan values cannot revert a NaN
+        double[] testArray = {Double.NaN, +0.0d, -0.0d, 
Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
+        Max stat = Max.create();
+        for (final double x : testArray) {
+            stat.accept(x);
+            Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+        }
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMax(double[] values, double expected) {
+        Max stat = Max.create();
+        Arrays.stream(values).forEach(stat);
+        double actual = stat.getAsDouble();
+        Assertions.assertEquals(expected, actual, "max");
+        Assertions.assertEquals(expected, Max.of(values).getAsDouble(), "max");
+    }
+
+    static Stream<Arguments> testMax() {
+        return Stream.of(
+            Arguments.of(new double[] {}, Double.NEGATIVE_INFINITY),
+            Arguments.of(new double[] {3.14}, 3.14),
+            Arguments.of(new double[] {12.34, 56.78, -2.0}, 56.78),
+            Arguments.of(new double[] {Double.NaN, 3.14, Double.NaN, 
Double.NaN}, Double.NaN),
+            Arguments.of(new double[] {-1d, 1d, Double.NaN}, Double.NaN),
+            Arguments.of(new double[] {Double.NaN, Double.NaN, Double.NaN}, 
Double.NaN),
+            Arguments.of(new double[] {0.0d, Double.NaN, +0.0d, -0.0d, 
Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NaN),
+            Arguments.of(new double[] {+0.0d, -0.0d, 1.0, 3.14}, 3.14),
+            Arguments.of(new double[] {-0.0, +0.0}, 0.0),
+            Arguments.of(new double[] {0.0, -0.0}, 0.0),
+            Arguments.of(new double[] {0.0, +0.0}, 0.0),
+            Arguments.of(new double[] {1.2, -34.56, 456.789, -5678.9012}, 
456.789),
+            Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, 
-23492, -92397747}, 23749),
+            Arguments.of(new double[] {1.7976931348623157e+307, 
1.7976931348623157e+306, 0.0, Double.MAX_VALUE}, Double.MAX_VALUE),
+            Arguments.of(new double[] {1.7976931348623157e+307, 
1.7976931348623157e+306, 0.0, -Double.MAX_VALUE}, 1.7976931348623157e+307),
+            Arguments.of(new double[] {Double.NEGATIVE_INFINITY, 
-Double.MAX_VALUE, Double.MAX_VALUE, Double.POSITIVE_INFINITY}, 
Double.POSITIVE_INFINITY),
+            Arguments.of(new double[] {Double.NEGATIVE_INFINITY, 
-Double.MAX_VALUE, -Double.MIN_VALUE}, -Double.MIN_VALUE)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = "testMax")
+    void testParallelStream(double[] values, double expected) {
+        double actual = Arrays.stream(values).parallel().collect(Max::create, 
Max::accept, Max::combine).getAsDouble();
+        Assertions.assertEquals(expected, actual);
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = "testMax")
+    void testMaxRandomOrder(double[] values, double expected) {
+        UniformRandomProvider rng = TestHelper.createRNG();
+        for (int i = 0; i < 10; i++) {
+            testMax(TestHelper.shuffle(rng, values), expected);
+        }
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCombine(double[] first, double[] second, double expected) {
+        Max firstMax = Max.create();
+        Max secondMax = Max.create();
+
+        Arrays.stream(first).forEach(firstMax);
+        Arrays.stream(second).forEach(secondMax);
+
+        double secondMaxBeforeCombine = secondMax.getAsDouble();
+        firstMax.combine(secondMax);
+        Assertions.assertEquals(expected, firstMax.getAsDouble());
+        Assertions.assertEquals(secondMaxBeforeCombine, 
secondMax.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+            Arguments.of(new double[] {}, new double[] {}, 
Double.NEGATIVE_INFINITY),
+            Arguments.of(new double[] {3.14}, new double[] {}, 3.14),
+            Arguments.of(new double[] {}, new double[] {2.718}, 2.718),
+            Arguments.of(new double[] {}, new double[] {Double.NaN}, 
Double.NaN),
+            Arguments.of(new double[] {Double.NaN, Double.NaN}, new double[] 
{}, Double.NaN),
+            Arguments.of(new double[] {3.14}, new double[] {2.718}, 3.14),
+            Arguments.of(new double[] {-1, 0, 1}, new double[] {1.1, 2.2, 
3.3}, 3.3),
+            Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 
1.1, 333.333}, 333.333),
+            Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 
23.45}, 56.78),
+            Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] 
{1.1}, 333.333),
+            Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 
2.718, -0.0}, 22.22),
+            Arguments.of(new double[] {0.0, -Double.MAX_VALUE, 
Double.POSITIVE_INFINITY},
+                new double[] {Double.NEGATIVE_INFINITY, -0.0, 
Double.NEGATIVE_INFINITY, Double.MAX_VALUE},
+                Double.POSITIVE_INFINITY),
+            Arguments.of(new double[] {0.0, Double.NaN, -Double.MAX_VALUE, 
Double.POSITIVE_INFINITY},
+                new double[] {Double.NaN, -0.0, Double.NaN, 
Double.NEGATIVE_INFINITY, Double.MAX_VALUE},
+                Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testArrayOfArrays(double[][] input, double expected) {
+        double actual = Arrays.stream(input)
+                .map(Max::of)
+                .reduce(Max::combine)
+                .map(DoubleSupplier::getAsDouble)
+                .orElseThrow(RuntimeException::new);
+
+        Assertions.assertEquals(expected, actual);
+    }
+
+    static Stream<Arguments> testArrayOfArrays() {
+        return Stream.of(
+                Arguments.of(new double[][] {{}, {}, {}}, 
Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[][] {{}, {Double.NaN}, {-1.7}}, 
Double.NaN),
+                Arguments.of(new double[][] {{}, {Double.NaN}, {}}, 
Double.NaN),
+                Arguments.of(new double[][] {{}, {1.1, 2}, {-1.7}}, 2),
+                Arguments.of(new double[][] {{1, 2}, {3, 4}}, 4),
+                Arguments.of(new double[][] {{+0.0, 2.0}, {1.0, -0.0, 3.14}}, 
3.14),
+                Arguments.of(new double[][] {{+0.0, Double.NEGATIVE_INFINITY}, 
{-0.0, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}}, 
Double.POSITIVE_INFINITY),
+                Arguments.of(new double[][] {{1.1, 22.22}, {34.56, -5678.9, 
2.718}, {Double.NaN, 0}},
+                        Double.NaN),
+                Arguments.of(new double[][] {{Double.NaN, Double.NaN}, 
{Double.NaN}, {Double.NaN, Double.NaN, Double.NaN}}, Double.NaN)
+        );
+    }
+}
diff --git a/src/conf/pmd/pmd-ruleset.xml b/src/conf/pmd/pmd-ruleset.xml
index 8bfff93..03bb30b 100644
--- a/src/conf/pmd/pmd-ruleset.xml
+++ b/src/conf/pmd/pmd-ruleset.xml
@@ -90,7 +90,7 @@
     <properties>
       <property name="violationSuppressXPath"
         value="./ancestor-or-self::ClassOrInterfaceDeclaration[@SimpleName='DD'
-          or @SimpleName='Two' or @SimpleName='One' or @SimpleName='Min']"/>
+          or @SimpleName='Two' or @SimpleName='One' or @SimpleName='Min' or 
@SimpleName='Max']"/>
     </properties>
   </rule>
   <rule ref="category/java/codestyle.xml/PrematureDeclaration">

Reply via email to