You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "ani5rudh (via GitHub)" <gi...@apache.org> on 2023/07/07 17:55:26 UTC

[GitHub] [commons-statistics] ani5rudh opened a new pull request, #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

ani5rudh opened a new pull request, #46:
URL: https://github.com/apache/commons-statistics/pull/46

   (no comment)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] kinow commented on pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "kinow (via GitHub)" <gi...@apache.org>.
kinow commented on PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#issuecomment-1636880593

   @ani5rudh , why did you close this pull request and created a new one? I thought you would rebase, and perhaps squash it with Git. After that this could be merged and others would be able to find a pull request with the discussion and history of the change here. If you create separate pull requests it becomes much harder.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] kinow commented on a diff in pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "kinow (via GitHub)" <gi...@apache.org>.
kinow commented on code in PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#discussion_r1256568342


##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Statistic.java:
##########
@@ -0,0 +1,28 @@
+package org.apache.commons.statistics.descriptive;
+
+/**
+ * Represents a statistic being computed.
+ */
+public enum Statistic {
+    MIN,
+
+    MAX,
+
+    SUM,
+
+    MEAN,
+
+    SUM_OF_LOGS,
+
+    SUM_OF_SQUARES,
+
+    VARIANCE,
+
+    POPULATION_VARIANCE,
+
+    STANDARD_DEVIATION,
+
+    GEOMETRIC_MEAN,
+
+    QUADRATIC_MEAN

Review Comment:
   I believe the statistic enum values can also be documented.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] kinow commented on pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "kinow (via GitHub)" <gi...@apache.org>.
kinow commented on PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#issuecomment-1636997295

   This branch is showing 0 commits now. You probably have to push what you have in the other branch to this pull request and maybe close the other PR (hovering the reopen pull request a message is displayed mentioning another PR).


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] aherbert commented on a diff in pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "aherbert (via GitHub)" <gi...@apache.org>.
aherbert commented on code in PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#discussion_r1262438649


##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,141 @@
+/*
+ * 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 minimum 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>POSITIVE_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.
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Create a Min instance.
+     */
+    Min() {
+        // No-op
+    }
+
+    /**
+     * Creates a {@code Min} 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#POSITIVE_INFINITY POSITIVE_INFINITY}
+     * if no values have been added.
+     *
+     * @return {@code Min} implementation
+     *

Review Comment:
   Remove this empty line



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/DoubleStatistic.java:
##########
@@ -0,0 +1,29 @@
+/*
+ * 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.DoubleConsumer;
+import java.util.function.DoubleSupplier;
+
+
+/**
+ * Represents a state object for computing a single {@code Statistic} over {@code double} valued input(s).
+ *
+ * <p>Base interface implemented by all statistics.</p>

Review Comment:
   Check for and remove `</p>` tags



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,141 @@
+/*
+ * 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 minimum 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>POSITIVE_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.
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Create a Min instance.
+     */
+    Min() {
+        // No-op
+    }
+
+    /**
+     * Creates a {@code Min} 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#POSITIVE_INFINITY POSITIVE_INFINITY}
+     * if no values have been added.
+     *
+     * @return {@code Min} implementation
+     *
+     */
+    public static Min create() {
+        return new StorelessMin();
+    }
+
+    /**
+     * Returns a {@code Min} instance that has the minimum 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#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @param values Values
+     * @return Min instance
+     */
+    public static Min of(double... values) {
+        final StorelessMin storelessMin = new StorelessMin();

Review Comment:
   Change `storelessMin` to `min`



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,141 @@
+/*
+ * 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 minimum 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>POSITIVE_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.
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Create a Min instance.
+     */
+    Min() {
+        // No-op
+    }
+
+    /**
+     * Creates a {@code Min} 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#POSITIVE_INFINITY POSITIVE_INFINITY}
+     * if no values have been added.
+     *
+     * @return {@code Min} implementation
+     *
+     */
+    public static Min create() {
+        return new StorelessMin();
+    }
+
+    /**
+     * Returns a {@code Min} instance that has the minimum 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#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @param values Values
+     * @return Min instance
+     */
+    public static Min of(double... values) {
+        final StorelessMin storelessMin = new StorelessMin();
+        Arrays.stream(values).forEach(storelessMin);
+        return storelessMin;
+    }
+
+    /**
+     * 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 minimum of all input values.
+     *
+     * <p>When no values have been added, the result is
+     * {@link Double#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @return The {@code min}.
+     */
+    @Override
+    public abstract double getAsDouble();
+
+    /** {@inheritDoc} */
+    @Override
+    public abstract Min combine(Min other);
+
+    /**
+     * {@code Min} implementation that does not store the input value(s) processed so far.
+     *
+     * <p>Uses JDK's {@link Math#min Math.min} as an underlying function
+     * to compute the {@code minimum}
+     */
+    private static final class StorelessMin extends Min {

Review Comment:
   IIUC this does not need the final keyword as it is private. It may be linting tools from Sonarcloud that highlight this.



##########
src/conf/pmd/pmd-ruleset.xml:
##########
@@ -90,7 +90,8 @@
     <properties>
       <property name="violationSuppressXPath"
         value="./ancestor-or-self::ClassOrInterfaceDeclaration[@SimpleName='DD'
-          or @SimpleName='Two' or @SimpleName='One']"/>
+          or @SimpleName='Two' or @SimpleName='One' or @SimpleName='Min']"/>
+

Review Comment:
   Remove this empty line



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,141 @@
+/*
+ * 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 minimum 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>POSITIVE_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.
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Create a Min instance.
+     */
+    Min() {
+        // No-op
+    }
+
+    /**
+     * Creates a {@code Min} 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#POSITIVE_INFINITY POSITIVE_INFINITY}
+     * if no values have been added.
+     *
+     * @return {@code Min} implementation
+     *
+     */
+    public static Min create() {
+        return new StorelessMin();
+    }
+
+    /**
+     * Returns a {@code Min} instance that has the minimum 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#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @param values Values

Review Comment:
   End param descriptions with a period `.`



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,221 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actualMin = stat.getAsDouble();

Review Comment:
   Here the `Min` is redundant from expected and actual. I would drop it for cleaner code. It makes it simpler to adapt the test to other statistics by copy.



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,221 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actualMin = stat.getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin, "min");
+        Assertions.assertEquals(expectedMin, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {Double.NaN, 3.14, Double.NaN, Double.NaN}, Double.NaN),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCombine(double[] first, double[] second, double expectedMin) {
+        Min firstMin = Min.create();
+        Min secondMin = Min.create();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        double secondMinBeforeCombine = secondMin.getAsDouble();
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expectedMin, firstMin.getAsDouble());
+        Assertions.assertEquals(secondMinBeforeCombine, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {}, new double[] {}, Double.POSITIVE_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}, 2.718),
+                Arguments.of(new double[] {-1, 0, 1}, new double[] {1.1, 2.2, 3.3}, -1),
+                Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 1.1, 333.333}, 1.1),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 23.45}, -2.0),
+                Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] {1.1}, -2023.79),
+                Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 2.718, -0.0}, -0.0),
+                Arguments.of(new double[] {0.0, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NEGATIVE_INFINITY, -0.0, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0, Double.NaN, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NaN, -0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testParallelStream(double[] values, double expectedMin) {
+        double actualMin = Arrays.stream(values).parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin);
+    }
+
+    static Stream<Arguments> testParallelStream() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {1.2, -34.56, 456.789, -5678.9012}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747),
+                Arguments.of(new double[] {-1d, 1d, Double.NaN}, Double.NaN),
+                Arguments.of(new double[] {+0.0d, -0.0d, 1.0, 3.14}, -0.0d),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0d, Double.NaN, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NaN)
+        );
+    }
+
+    @Test
+    void testNanParallelStream() {
+        DoubleStream nanStream = DoubleStream.of(Double.NaN, Double.NaN, Double.NaN);
+        Assertions.assertTrue(Double.isNaN(nanStream.parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble()));
+    }
+
+    @Test
+    void testSpecialValues() {
+        double[] testArray = {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
+        Min stat = Min.create();
+
+        stat.accept(testArray[0]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[1]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[2]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[3]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[4]);
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, stat.getAsDouble());
+
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, Min.of(testArray).getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testNaNs(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        for (final double value: values) {
+            stat.accept(value);
+        }
+        Assertions.assertEquals(expectedMin, stat.getAsDouble());
+    }
+
+    static Stream<Arguments> testNaNs() {
+        return Stream.of(
+                Arguments.of(new double[] {Double.NaN, 2d, 3d}, Double.NaN),
+                Arguments.of(new double[] {1d, Double.NaN, 3d}, Double.NaN),
+                Arguments.of(new double[] {-1d, 1d, Double.NaN}, Double.NaN)
+        );
+    }
+
+    @Test
+    void testNaN() {
+        double[] testArray = {0.0d, Double.NaN, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
+        Min stat = Min.create();
+
+        stat.accept(testArray[0]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[1]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[2]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[3]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[4]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[5]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        Assertions.assertEquals(Double.NaN, Min.of(testArray).getAsDouble());

Review Comment:
   This `Min.of` test can be covered by moving this array into the cases for `testMin`.
   
   Essentially what this test is doing is checking if any special non-nan value _cannot_ change a NaN back to a non-nan value. Perhaps this is easier to read using:
   ```java
   @Test
   void testNaN() {
       double[] testArray = {Double.NaN, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
       Min stat = Min.create();
       // Test non-nan values cannot revert a NaN
       for (final double x : testArray) {
           stat.accept(x);
           Assertions.assertEquals(Double.NaN, stat.getAsDouble());
       }
   }
   ```



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,221 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actualMin = stat.getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin, "min");
+        Assertions.assertEquals(expectedMin, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {Double.NaN, 3.14, Double.NaN, Double.NaN}, Double.NaN),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCombine(double[] first, double[] second, double expectedMin) {
+        Min firstMin = Min.create();
+        Min secondMin = Min.create();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        double secondMinBeforeCombine = secondMin.getAsDouble();
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expectedMin, firstMin.getAsDouble());
+        Assertions.assertEquals(secondMinBeforeCombine, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {}, new double[] {}, Double.POSITIVE_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}, 2.718),
+                Arguments.of(new double[] {-1, 0, 1}, new double[] {1.1, 2.2, 3.3}, -1),
+                Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 1.1, 333.333}, 1.1),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 23.45}, -2.0),
+                Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] {1.1}, -2023.79),
+                Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 2.718, -0.0}, -0.0),
+                Arguments.of(new double[] {0.0, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NEGATIVE_INFINITY, -0.0, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0, Double.NaN, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NaN, -0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testParallelStream(double[] values, double expectedMin) {
+        double actualMin = Arrays.stream(values).parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin);
+    }
+
+    static Stream<Arguments> testParallelStream() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {1.2, -34.56, 456.789, -5678.9012}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747),
+                Arguments.of(new double[] {-1d, 1d, Double.NaN}, Double.NaN),
+                Arguments.of(new double[] {+0.0d, -0.0d, 1.0, 3.14}, -0.0d),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0d, Double.NaN, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NaN)
+        );
+    }
+
+    @Test
+    void testNanParallelStream() {
+        DoubleStream nanStream = DoubleStream.of(Double.NaN, Double.NaN, Double.NaN);
+        Assertions.assertTrue(Double.isNaN(nanStream.parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble()));
+    }
+
+    @Test
+    void testSpecialValues() {
+        double[] testArray = {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
+        Min stat = Min.create();
+
+        stat.accept(testArray[0]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[1]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[2]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[3]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[4]);
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, stat.getAsDouble());
+
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, Min.of(testArray).getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testNaNs(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        for (final double value: values) {
+            stat.accept(value);
+        }
+        Assertions.assertEquals(expectedMin, stat.getAsDouble());
+    }
+
+    static Stream<Arguments> testNaNs() {
+        return Stream.of(
+                Arguments.of(new double[] {Double.NaN, 2d, 3d}, Double.NaN),
+                Arguments.of(new double[] {1d, Double.NaN, 3d}, Double.NaN),
+                Arguments.of(new double[] {-1d, 1d, Double.NaN}, Double.NaN)
+        );
+    }
+
+    @Test
+    void testNaN() {
+        double[] testArray = {0.0d, Double.NaN, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
+        Min stat = Min.create();
+
+        stat.accept(testArray[0]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[1]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[2]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[3]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[4]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[5]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        Assertions.assertEquals(Double.NaN, Min.of(testArray).getAsDouble());
+
+        Assertions.assertTrue(Double.isNaN(Min.of(Double.NaN, Double.NaN, Double.NaN).getAsDouble()));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testArrayOfArrays(double[][] input, double expectedMin) {
+
+        double actualMin = Arrays.stream(input)
+                .map(Min::of)
+                .reduce(Min::combine)
+                .map(DoubleSupplier::getAsDouble)
+                .orElseThrow(RuntimeException::new);
+
+        Assertions.assertEquals(expectedMin, actualMin);
+    }
+    static Stream<Arguments> testArrayOfArrays() {

Review Comment:
   Empty line above the method declaration.
   



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,221 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actualMin = stat.getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin, "min");
+        Assertions.assertEquals(expectedMin, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {Double.NaN, 3.14, Double.NaN, Double.NaN}, Double.NaN),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCombine(double[] first, double[] second, double expectedMin) {
+        Min firstMin = Min.create();
+        Min secondMin = Min.create();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        double secondMinBeforeCombine = secondMin.getAsDouble();
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expectedMin, firstMin.getAsDouble());
+        Assertions.assertEquals(secondMinBeforeCombine, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {}, new double[] {}, Double.POSITIVE_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}, 2.718),
+                Arguments.of(new double[] {-1, 0, 1}, new double[] {1.1, 2.2, 3.3}, -1),
+                Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 1.1, 333.333}, 1.1),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 23.45}, -2.0),
+                Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] {1.1}, -2023.79),
+                Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 2.718, -0.0}, -0.0),
+                Arguments.of(new double[] {0.0, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NEGATIVE_INFINITY, -0.0, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0, Double.NaN, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NaN, -0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testParallelStream(double[] values, double expectedMin) {
+        double actualMin = Arrays.stream(values).parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin);
+    }
+
+    static Stream<Arguments> testParallelStream() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {1.2, -34.56, 456.789, -5678.9012}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747),
+                Arguments.of(new double[] {-1d, 1d, Double.NaN}, Double.NaN),
+                Arguments.of(new double[] {+0.0d, -0.0d, 1.0, 3.14}, -0.0d),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0d, Double.NaN, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NaN)
+        );
+    }
+
+    @Test
+    void testNanParallelStream() {
+        DoubleStream nanStream = DoubleStream.of(Double.NaN, Double.NaN, Double.NaN);
+        Assertions.assertTrue(Double.isNaN(nanStream.parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble()));
+    }
+
+    @Test
+    void testSpecialValues() {
+        double[] testArray = {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
+        Min stat = Min.create();
+
+        stat.accept(testArray[0]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[1]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[2]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[3]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[4]);
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, stat.getAsDouble());
+
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, Min.of(testArray).getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testNaNs(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        for (final double value: values) {
+            stat.accept(value);
+        }
+        Assertions.assertEquals(expectedMin, stat.getAsDouble());
+    }
+
+    static Stream<Arguments> testNaNs() {
+        return Stream.of(
+                Arguments.of(new double[] {Double.NaN, 2d, 3d}, Double.NaN),
+                Arguments.of(new double[] {1d, Double.NaN, 3d}, Double.NaN),
+                Arguments.of(new double[] {-1d, 1d, Double.NaN}, Double.NaN)
+        );
+    }
+
+    @Test
+    void testNaN() {
+        double[] testArray = {0.0d, Double.NaN, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
+        Min stat = Min.create();
+
+        stat.accept(testArray[0]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[1]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[2]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[3]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[4]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[5]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        Assertions.assertEquals(Double.NaN, Min.of(testArray).getAsDouble());
+
+        Assertions.assertTrue(Double.isNaN(Min.of(Double.NaN, Double.NaN, Double.NaN).getAsDouble()));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testArrayOfArrays(double[][] input, double expectedMin) {
+
+        double actualMin = Arrays.stream(input)
+                .map(Min::of)
+                .reduce(Min::combine)
+                .map(DoubleSupplier::getAsDouble)
+                .orElseThrow(RuntimeException::new);
+
+        Assertions.assertEquals(expectedMin, actualMin);
+    }
+    static Stream<Arguments> testArrayOfArrays() {
+        return Stream.of(
+                Arguments.of(new double[][] {{}, {}, {}}, Double.POSITIVE_INFINITY),

Review Comment:
   Note: You can use 4 or 8 space chars for the trailing line. When the lines are long using 4 chars allows more to fit in a standard text editor.



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,221 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actualMin = stat.getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin, "min");
+        Assertions.assertEquals(expectedMin, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {Double.NaN, 3.14, Double.NaN, Double.NaN}, Double.NaN),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCombine(double[] first, double[] second, double expectedMin) {
+        Min firstMin = Min.create();
+        Min secondMin = Min.create();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        double secondMinBeforeCombine = secondMin.getAsDouble();
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expectedMin, firstMin.getAsDouble());
+        Assertions.assertEquals(secondMinBeforeCombine, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {}, new double[] {}, Double.POSITIVE_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}, 2.718),
+                Arguments.of(new double[] {-1, 0, 1}, new double[] {1.1, 2.2, 3.3}, -1),
+                Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 1.1, 333.333}, 1.1),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 23.45}, -2.0),
+                Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] {1.1}, -2023.79),
+                Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 2.718, -0.0}, -0.0),
+                Arguments.of(new double[] {0.0, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NEGATIVE_INFINITY, -0.0, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0, Double.NaN, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NaN, -0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testParallelStream(double[] values, double expectedMin) {
+        double actualMin = Arrays.stream(values).parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin);
+    }
+
+    static Stream<Arguments> testParallelStream() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {1.2, -34.56, 456.789, -5678.9012}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747),
+                Arguments.of(new double[] {-1d, 1d, Double.NaN}, Double.NaN),
+                Arguments.of(new double[] {+0.0d, -0.0d, 1.0, 3.14}, -0.0d),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0d, Double.NaN, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NaN)
+        );
+    }
+
+    @Test
+    void testNanParallelStream() {
+        DoubleStream nanStream = DoubleStream.of(Double.NaN, Double.NaN, Double.NaN);
+        Assertions.assertTrue(Double.isNaN(nanStream.parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble()));
+    }
+
+    @Test
+    void testSpecialValues() {
+        double[] testArray = {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
+        Min stat = Min.create();
+
+        stat.accept(testArray[0]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[1]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[2]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[3]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[4]);
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, stat.getAsDouble());
+
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, Min.of(testArray).getAsDouble());

Review Comment:
   I would remove this `Min.of` and add this testArray to the cases for testMin. The incremental nature of the assertions in this test are not easy to follow. The same data with special values is in testParallelStream so there is redundancy here. If you wish to add a test for incremental min then perhaps it is more clear with counters:
   ```java
   @Test
   void testIncrement() {
       int lo = 0;
       int hi = 0;
       final Min stat = Min.of(lo);
       for (int i = 0; i < 5; i++) {
           stat.accept(++hi);
           Assertions.assertEquals(lo, stat.getAsDouble());
           stat.accept(--lo);
           Assertions.assertEquals(lo, stat.getAsDouble());
       }
   }
   ```



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,141 @@
+/*
+ * 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 minimum 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>POSITIVE_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.
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Create a Min instance.
+     */
+    Min() {
+        // No-op
+    }
+
+    /**
+     * Creates a {@code Min} 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#POSITIVE_INFINITY POSITIVE_INFINITY}
+     * if no values have been added.
+     *
+     * @return {@code Min} implementation
+     *
+     */
+    public static Min create() {
+        return new StorelessMin();
+    }
+
+    /**
+     * Returns a {@code Min} instance that has the minimum 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#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @param values Values
+     * @return Min instance
+     */
+    public static Min of(double... values) {
+        final StorelessMin storelessMin = new StorelessMin();
+        Arrays.stream(values).forEach(storelessMin);
+        return storelessMin;
+    }
+
+    /**
+     * 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 minimum of all input values.
+     *
+     * <p>When no values have been added, the result is
+     * {@link Double#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @return The {@code min}.
+     */
+    @Override
+    public abstract double getAsDouble();
+
+    /** {@inheritDoc} */
+    @Override
+    public abstract Min combine(Min other);
+
+    /**
+     * {@code Min} implementation that does not store the input value(s) processed so far.
+     *
+     * <p>Uses JDK's {@link Math#min Math.min} as an underlying function
+     * to compute the {@code minimum}
+     */
+    private static final class StorelessMin extends Min {
+
+        /** Current min. */
+        private double min;
+
+        /** Create a StorelessMin instance. */
+        StorelessMin() {
+            min = Double.POSITIVE_INFINITY;
+        }
+
+        /** {@inheritDoc} */

Review Comment:
   I think that internal classes may not require the inheritDoc javadoc lines. Javadoc like this is only required on public classes as the final javadoc report does not process private scope.



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,141 @@
+/*
+ * 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 minimum 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>POSITIVE_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.
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Create a Min instance.
+     */
+    Min() {
+        // No-op
+    }
+
+    /**
+     * Creates a {@code Min} 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#POSITIVE_INFINITY POSITIVE_INFINITY}
+     * if no values have been added.
+     *
+     * @return {@code Min} implementation
+     *
+     */
+    public static Min create() {
+        return new StorelessMin();
+    }
+
+    /**
+     * Returns a {@code Min} instance that has the minimum 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#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @param values Values
+     * @return Min instance
+     */
+    public static Min of(double... values) {
+        final StorelessMin storelessMin = new StorelessMin();
+        Arrays.stream(values).forEach(storelessMin);

Review Comment:
   It may be interesting to use JMH to compare the use of streams to a simple for loop here, e.g.
   ```java
   for (final double x : values) {
       min.accept(x);
   }
   ```
   For now I would create a Jira ticket as a task to create JMH benchmarks for statistic initialisation comparing Arrays.stream to a simple for loop.
   



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,141 @@
+/*
+ * 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 minimum 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>POSITIVE_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.
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Create a Min instance.
+     */
+    Min() {
+        // No-op
+    }
+
+    /**
+     * Creates a {@code Min} 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#POSITIVE_INFINITY POSITIVE_INFINITY}
+     * if no values have been added.
+     *
+     * @return {@code Min} implementation
+     *
+     */
+    public static Min create() {
+        return new StorelessMin();
+    }
+
+    /**
+     * Returns a {@code Min} instance that has the minimum 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#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @param values Values
+     * @return Min instance
+     */
+    public static Min of(double... values) {
+        final StorelessMin storelessMin = new StorelessMin();
+        Arrays.stream(values).forEach(storelessMin);
+        return storelessMin;
+    }
+
+    /**
+     * 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 minimum of all input values.
+     *
+     * <p>When no values have been added, the result is
+     * {@link Double#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @return The {@code min}.
+     */
+    @Override
+    public abstract double getAsDouble();
+
+    /** {@inheritDoc} */
+    @Override
+    public abstract Min combine(Min other);
+
+    /**
+     * {@code Min} implementation that does not store the input value(s) processed so far.
+     *
+     * <p>Uses JDK's {@link Math#min Math.min} as an underlying function
+     * to compute the {@code minimum}

Review Comment:
   Period at end of sentence.



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,141 @@
+/*
+ * 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 minimum 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>POSITIVE_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.
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Create a Min instance.
+     */
+    Min() {
+        // No-op
+    }
+
+    /**
+     * Creates a {@code Min} 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#POSITIVE_INFINITY POSITIVE_INFINITY}
+     * if no values have been added.
+     *
+     * @return {@code Min} implementation
+     *
+     */
+    public static Min create() {
+        return new StorelessMin();
+    }
+
+    /**
+     * Returns a {@code Min} instance that has the minimum 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#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @param values Values
+     * @return Min instance
+     */
+    public static Min of(double... values) {
+        final StorelessMin storelessMin = new StorelessMin();
+        Arrays.stream(values).forEach(storelessMin);
+        return storelessMin;
+    }
+
+    /**
+     * 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 minimum of all input values.
+     *
+     * <p>When no values have been added, the result is
+     * {@link Double#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @return The {@code min}.
+     */
+    @Override
+    public abstract double getAsDouble();
+
+    /** {@inheritDoc} */
+    @Override
+    public abstract Min combine(Min other);
+
+    /**
+     * {@code Min} implementation that does not store the input value(s) processed so far.
+     *
+     * <p>Uses JDK's {@link Math#min Math.min} as an underlying function
+     * to compute the {@code minimum}
+     */
+    private static final class StorelessMin extends Min {
+
+        /** Current min. */
+        private double min;
+
+        /** Create a StorelessMin instance. */

Review Comment:
   In general you can use the shorter "Create an instance" for required but otherwise uninformative constructor docs.
   
   Since you do not use this constructor I would drop it and initialise the `min` directly in the declaration:
   ```
   private double min = Double.POSITIVE_INFINITY;
   ```



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/DoubleStatisticAccumulator.java:
##########
@@ -0,0 +1,33 @@
+/*
+ * 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 {@code DoubleStatistic}.
+ *
+ * @param <T>  the {@code DoubleStatistic} being accumulated.
+ */
+public interface DoubleStatisticAccumulator<T extends DoubleStatistic> {
+
+    /**
+     * Combines the state of another {@code DoubleStatistic} into this one.
+     *
+     * @param other another {@code DoubleStatistic} to be combined

Review Comment:
   This project uses capitalisation for the first word after the param name. It does not use `the` as the first word (unlike the JDK). All sentences should end with a period. So this should change to:
   ```
   @param other Another {@code DoubleStatistic} to be combined.
   // or
   @param other {@code DoubleStatistic} to be combined.
   ```
   
   Strangely the Commons math projects are not so strict on the `@return` tag and allow `the`. However it is good to maintain a consistent style.
   
   In this case we could select wording from any builder or other object that returns itself, which typically includes the word `this` for example:
   ```
   @return {@code this} instance
   ```
   



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,221 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actualMin = stat.getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin, "min");
+        Assertions.assertEquals(expectedMin, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {Double.NaN, 3.14, Double.NaN, Double.NaN}, Double.NaN),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCombine(double[] first, double[] second, double expectedMin) {
+        Min firstMin = Min.create();
+        Min secondMin = Min.create();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        double secondMinBeforeCombine = secondMin.getAsDouble();
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expectedMin, firstMin.getAsDouble());
+        Assertions.assertEquals(secondMinBeforeCombine, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {}, new double[] {}, Double.POSITIVE_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}, 2.718),
+                Arguments.of(new double[] {-1, 0, 1}, new double[] {1.1, 2.2, 3.3}, -1),
+                Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 1.1, 333.333}, 1.1),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 23.45}, -2.0),
+                Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] {1.1}, -2023.79),
+                Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 2.718, -0.0}, -0.0),
+                Arguments.of(new double[] {0.0, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NEGATIVE_INFINITY, -0.0, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0, Double.NaN, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NaN, -0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testParallelStream(double[] values, double expectedMin) {
+        double actualMin = Arrays.stream(values).parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin);
+    }
+
+    static Stream<Arguments> testParallelStream() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {1.2, -34.56, 456.789, -5678.9012}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747),
+                Arguments.of(new double[] {-1d, 1d, Double.NaN}, Double.NaN),
+                Arguments.of(new double[] {+0.0d, -0.0d, 1.0, 3.14}, -0.0d),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0d, Double.NaN, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NaN)
+        );
+    }
+
+    @Test
+    void testNanParallelStream() {
+        DoubleStream nanStream = DoubleStream.of(Double.NaN, Double.NaN, Double.NaN);
+        Assertions.assertTrue(Double.isNaN(nanStream.parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble()));
+    }
+
+    @Test
+    void testSpecialValues() {
+        double[] testArray = {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
+        Min stat = Min.create();
+
+        stat.accept(testArray[0]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[1]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[2]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[3]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[4]);
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, stat.getAsDouble());
+
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, Min.of(testArray).getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testNaNs(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        for (final double value: values) {
+            stat.accept(value);
+        }
+        Assertions.assertEquals(expectedMin, stat.getAsDouble());
+    }
+
+    static Stream<Arguments> testNaNs() {

Review Comment:
   These test cases and test fixture can be combined into the cases for testMin.



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,221 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actualMin = stat.getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin, "min");
+        Assertions.assertEquals(expectedMin, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {Double.NaN, 3.14, Double.NaN, Double.NaN}, Double.NaN),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCombine(double[] first, double[] second, double expectedMin) {
+        Min firstMin = Min.create();
+        Min secondMin = Min.create();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        double secondMinBeforeCombine = secondMin.getAsDouble();
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expectedMin, firstMin.getAsDouble());
+        Assertions.assertEquals(secondMinBeforeCombine, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {}, new double[] {}, Double.POSITIVE_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}, 2.718),
+                Arguments.of(new double[] {-1, 0, 1}, new double[] {1.1, 2.2, 3.3}, -1),
+                Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 1.1, 333.333}, 1.1),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 23.45}, -2.0),
+                Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] {1.1}, -2023.79),
+                Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 2.718, -0.0}, -0.0),
+                Arguments.of(new double[] {0.0, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NEGATIVE_INFINITY, -0.0, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0, Double.NaN, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NaN, -0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testParallelStream(double[] values, double expectedMin) {
+        double actualMin = Arrays.stream(values).parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin);
+    }
+
+    static Stream<Arguments> testParallelStream() {

Review Comment:
   This method contains different cases to testMin. They should be merged to capture all cases. You should add an empty array as a values input.
   
   You can then test each method using the same streaming method, e.g.:
   ```java
   @ParameterizedTest
   @MethodSource(value = "testMin")
   void testParallelStream(double[] values, double expectedMin) {
   ```
   



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,221 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actualMin = stat.getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin, "min");
+        Assertions.assertEquals(expectedMin, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {Double.NaN, 3.14, Double.NaN, Double.NaN}, Double.NaN),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCombine(double[] first, double[] second, double expectedMin) {
+        Min firstMin = Min.create();
+        Min secondMin = Min.create();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        double secondMinBeforeCombine = secondMin.getAsDouble();
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expectedMin, firstMin.getAsDouble());
+        Assertions.assertEquals(secondMinBeforeCombine, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {}, new double[] {}, Double.POSITIVE_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}, 2.718),
+                Arguments.of(new double[] {-1, 0, 1}, new double[] {1.1, 2.2, 3.3}, -1),
+                Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 1.1, 333.333}, 1.1),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 23.45}, -2.0),
+                Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] {1.1}, -2023.79),
+                Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 2.718, -0.0}, -0.0),
+                Arguments.of(new double[] {0.0, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NEGATIVE_INFINITY, -0.0, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0, Double.NaN, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NaN, -0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testParallelStream(double[] values, double expectedMin) {
+        double actualMin = Arrays.stream(values).parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin);
+    }
+
+    static Stream<Arguments> testParallelStream() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {1.2, -34.56, 456.789, -5678.9012}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747),
+                Arguments.of(new double[] {-1d, 1d, Double.NaN}, Double.NaN),
+                Arguments.of(new double[] {+0.0d, -0.0d, 1.0, 3.14}, -0.0d),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0d, Double.NaN, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NaN)
+        );
+    }
+
+    @Test
+    void testNanParallelStream() {
+        DoubleStream nanStream = DoubleStream.of(Double.NaN, Double.NaN, Double.NaN);
+        Assertions.assertTrue(Double.isNaN(nanStream.parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble()));
+    }
+
+    @Test
+    void testSpecialValues() {
+        double[] testArray = {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
+        Min stat = Min.create();
+
+        stat.accept(testArray[0]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[1]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[2]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[3]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[4]);
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, stat.getAsDouble());
+
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, Min.of(testArray).getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testNaNs(double[] values, double expectedMin) {
+        Min stat = Min.create();
+        for (final double value: values) {
+            stat.accept(value);
+        }
+        Assertions.assertEquals(expectedMin, stat.getAsDouble());
+    }
+
+    static Stream<Arguments> testNaNs() {
+        return Stream.of(
+                Arguments.of(new double[] {Double.NaN, 2d, 3d}, Double.NaN),
+                Arguments.of(new double[] {1d, Double.NaN, 3d}, Double.NaN),
+                Arguments.of(new double[] {-1d, 1d, Double.NaN}, Double.NaN)
+        );
+    }
+
+    @Test
+    void testNaN() {
+        double[] testArray = {0.0d, Double.NaN, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
+        Min stat = Min.create();
+
+        stat.accept(testArray[0]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[1]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[2]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[3]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[4]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        stat.accept(testArray[5]);
+        Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+
+        Assertions.assertEquals(Double.NaN, Min.of(testArray).getAsDouble());
+
+        Assertions.assertTrue(Double.isNaN(Min.of(Double.NaN, Double.NaN, Double.NaN).getAsDouble()));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testArrayOfArrays(double[][] input, double expectedMin) {
+

Review Comment:
   Remove empty line



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,221 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {

Review Comment:
   I think we should test that the same min is returned if the input array is randomised. I would add a TestHelper class with a Fisher-Yates shuffle copied from:
   [RNG ArraySampler](https://github.com/apache/commons-rng/blob/master/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/ArraySampler.java)
   
   If you update the signature to return the input argument then you can code:
   ```java
   @ParameterizedTest
   @MethodSource(value = "testMin")
   void testMinRandomOrder(double[] values, double expected) {
       UniformRandomProvider rng = RandomSource.SPLIT_MIX_64.create();
       for (int i = 0; i < 10; i++) {
           testMin(TestHelper.shuffle(rng, values.clone()), expected);
       }
   }
   ```
   
   The clone is only required if the `testMin` method destructively modifies the array. Otherwise you can continue to shuffle the same array each time.
   
   This change will require added the appropriate RNG modules to the pom.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] aherbert commented on pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "aherbert (via GitHub)" <gi...@apache.org>.
aherbert commented on PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#issuecomment-1637001699

   The `foundation` branch requires more commits to reopen this PR


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] aherbert commented on a diff in pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "aherbert (via GitHub)" <gi...@apache.org>.
aherbert commented on code in PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#discussion_r1258115764


##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+/**
+ * Returns the minimum of the available values.
+ *
+ * <p>The result is <code>NaN</code> if any of the values is <code>NaN</code>.</p>
+ * <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>
+ * <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.</p>
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Helper function that returns a new instance of {@link UnsupportedOperationException}.
+     * @return a new {@code UnsupportedOperationException} instance
+     */
+    static UnsupportedOperationException uoe() {

Review Comment:
   This saves a few characters of typing. The result however obfuscates the stack trace. I would drop the static method and throw the exception at the correct location. However since I would also drop the immutable class this is not required anymore.



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+/**
+ * Returns the minimum of the available values.
+ *
+ * <p>The result is <code>NaN</code> if any of the values is <code>NaN</code>.</p>
+ * <p>This class is designed to work with (though does not require)

Review Comment:
   Place an empty line here in the javadoc before the new paragraph.
   
   The style in this component is to not close `<p>` tags. Although not valid HTML 5 it is parsed correctly by the javadoc tool and a style used through the OpenJDK source code.



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+/**
+ * Returns the minimum of the available values.
+ *
+ * <p>The result is <code>NaN</code> if any of the values is <code>NaN</code>.</p>
+ * <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>
+ * <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.</p>
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Helper function that returns a new instance of {@link UnsupportedOperationException}.
+     * @return a new {@code UnsupportedOperationException} instance
+     */
+    static UnsupportedOperationException uoe() {
+        return new UnsupportedOperationException();
+    }
+
+    /**
+     * {@code Min} implementation that does not store the input value(s) processed so far.
+     */
+    private static final class StorelessMin extends Min {
+
+        /**Current min. */
+        private double min;
+
+        /**
+         * Create a StorelessMin instance.
+         */
+        StorelessMin() {
+            min = Double.POSITIVE_INFINITY;
+        }
+
+        /**
+         * Updates the state of Min statistic to reflect the addition of the new value.
+         * @param value  the new value.

Review Comment:
   It is not *the* new value, it is simply another value. This can be simplified to:
   ```
   * Updates the state of the statistic to reflect the addition of {@code value}.
   * @param value Value.
   ```
   As such on an internal class it is not adding much as you have to read the code to see this private comment. If you wish this to be seen in the documentation then you should promote the method to an abstract method in the `Min` class.
   
   You can see what is documented using:
   ```
   mvn javadoc:javadoc
   open target/site/apidocs/org/apache/commons/statistics/descriptive/Min.html
   ```
   
   Here the documentation is sparse. Try promoting the method as an abstract documented method in Min and note the difference.



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+/**
+ * Returns the minimum of the available values.
+ *
+ * <p>The result is <code>NaN</code> if any of the values is <code>NaN</code>.</p>
+ * <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>
+ * <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.</p>
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Helper function that returns a new instance of {@link UnsupportedOperationException}.
+     * @return a new {@code UnsupportedOperationException} instance
+     */
+    static UnsupportedOperationException uoe() {
+        return new UnsupportedOperationException();
+    }
+
+    /**
+     * {@code Min} implementation that does not store the input value(s) processed so far.
+     */
+    private static final class StorelessMin extends Min {
+
+        /**Current min. */

Review Comment:
   Missing a space: `/** Current`



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+/**
+ * Returns the minimum of the available values.
+ *
+ * <p>The result is <code>NaN</code> if any of the values is <code>NaN</code>.</p>
+ * <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>
+ * <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.</p>
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {

Review Comment:
   This class should have a package-private constructor.



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+/**
+ * Returns the minimum of the available values.
+ *
+ * <p>The result is <code>NaN</code> if any of the values is <code>NaN</code>.</p>
+ * <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>
+ * <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.</p>
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Helper function that returns a new instance of {@link UnsupportedOperationException}.
+     * @return a new {@code UnsupportedOperationException} instance
+     */
+    static UnsupportedOperationException uoe() {
+        return new UnsupportedOperationException();
+    }
+
+    /**
+     * {@code Min} implementation that does not store the input value(s) processed so far.
+     */
+    private static final class StorelessMin extends Min {
+
+        /**Current min. */
+        private double min;
+
+        /**
+         * Create a StorelessMin instance.
+         */
+        StorelessMin() {
+            min = Double.POSITIVE_INFINITY;
+        }
+
+        /**
+         * Updates the state of Min statistic to reflect the addition of the new value.
+         * @param value  the new value.
+         */
+        @Override
+        public void accept(double value) {
+            min = Double.min(min, value);
+        }
+
+        @Override
+        public double getAsDouble() {
+            return min;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public <U extends DoubleStatisticAccumulator<Min>> void combine(U other) {
+            final Min otherMin = other.getDoubleStatistic();
+            accept(otherMin.getAsDouble());
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Min getDoubleStatistic() {
+            return this;
+        }
+    }
+
+    /**
+     * Immutable {@code Min} implementation.
+     */
+    private static final class ImmutableMin extends Min {
+
+        /**Min delegate. */
+        private final Min delegate;
+
+        /**
+         * Initializes the Min delegate.
+         * @param delegate  Min delegate
+         */
+        ImmutableMin(final Min delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public void accept(double value) {
+            throw uoe();
+        }
+
+        @Override
+        public double getAsDouble() {
+            return delegate.getAsDouble();
+        }
+
+        @Override
+        public <U extends DoubleStatisticAccumulator<Min>> void combine(U other) {
+            throw uoe();
+        }
+
+        @Override
+        public Min getDoubleStatistic() {
+            return this;
+        }
+    }
+
+    /**
+     * Creates a {@code Min} implementation which does not store the input value(s) it consumes.
+     *
+     * @return {@code Min} implementation that does not store the input value(s) processed.
+     */
+    public static Min createStoreless() {
+        return new StorelessMin();
+    }
+
+    /**
+     * Returns an immutable {@code Min} object that has the minimum of all the input value(s).
+     *
+     * @param values  the input values for which we would need to compute the minimum
+     * @return Immutable Min implementation that does not store the input value(s) processed.
+     */
+    public static Min of(double... values) {

Review Comment:
   At present I do not see the requirement for the returned value to be immutable. This is a specialisation. For the general use case a mutable return value is more flexible.
   
   Consider this:
   ```java
   double[][] a = {{1, 2}, {3, 4}};
   double m1 = Arrays.stream(a)
                     .map(Min::of)
                     .reduce((x, y) -> {
                         x.combine(y);
                         return x;
                     }).get().getAsDouble();
   ```
   
   This does highlight an issue with the `DoubleStatisticAccumulator.combine` returning void not being compatible with the `reduce` method using a method reference. One solution is to have `combine` return `DoubleStatisticAccumulator<T>`. This leads to the clunky:
   ```java
   double m2 = Arrays.stream(a)
                    .map(x -> (DoubleStatisticAccumulator<Min>) Min.of(x))
                    .reduce(DoubleStatisticAccumulator::combine)
                    .get().getDoubleStatistic().getAsDouble();
   ```
   Another option is to not bother supporting the reduce method and use:
   ```java
   double m3 = Arrays.stream(a)
                     .flatMapToDouble(Arrays::stream)
                     .collect(Min::createStoreless, Min::accept, Min::combine)
                     .getAsDouble();
   ```
   The flatmap of the stream may be less efficient that immediately creating a stream of Min objects from the array.
   
   Or we can update `Min` to change the return type defined in the interface allowing:
   ```java
   // In Min
   @Override
   public abstract <U extends DoubleStatisticAccumulator<Min>> Min combine(U other);
   
   double m4 = Arrays.stream(a)
                     .map(Min::of)
                     .reduce(Min::combine)
                     .get().getAsDouble();
   ```



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {

Review Comment:
   Test fixtures can remove the `public` keyword as we are using JUnit 5



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Statistic.java:
##########
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+/**
+ * Represents a statistic being computed.
+ */
+public enum Statistic {
+    /** Represents the minimum of the input value(s). */
+    MIN,
+
+    /** Represents the maximum of the input value(s). */
+    MAX,
+
+    /** Represents the sum of the input value(s). */
+    SUM,
+
+    /** Represents the arithmetic mean of the input value(s). */
+    MEAN,
+
+    /** Represents the sum of the natural logs of the input value(s). */
+    SUM_OF_LOGS,
+
+    /** Represents the sum of the squares of the input value(s). */
+    SUM_OF_SQUARES,
+
+    /** Represents the sample variance of the input value(s). */
+    VARIANCE,
+
+    /** Represents the population variance of the input value(s). */
+    POPULATION_VARIANCE,
+
+    /** Represents the standard deviation of the input value(s). */
+    STANDARD_DEVIATION,

Review Comment:
   If you have sample and population variance then you should have the same for the standard deviation. Alternatively leave it to only 1 variance and plan to extend the options for a statistic with configuration of implementations (e.g. on creation).
   



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    public void testEmpty() {
+        Min min = Min.createStoreless();
+        Min immutable = Min.of();
+
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, immutable.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    public void testMin(double[] values, double expectedMin) {
+        Min stat = Min.createStoreless();
+        for (final double value: values) {
+            stat.accept(value);
+        }
+        double actualMin = stat.getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin, "min");
+        Assertions.assertEquals(expectedMin, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {Double.NaN, 3.14, Double.NaN, Double.NaN}, Double.NaN),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747)
+        );
+    }
+
+    @Test
+    public void testMinImmutable() {
+        Min stat = Min.of(1.0, 2.0, -1.0, 4.0);
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> stat.accept(3.14),
+                "cannot update an immutable instance");
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    public void testCombine(double[] first, double[] second, double expectedMin) {
+        Min firstMin = Min.createStoreless();
+        Min secondMin = Min.createStoreless();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expectedMin, firstMin.getAsDouble());

Review Comment:
   You should have an assertion that secondMin is unchanged by the combine operation.



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+/**
+ * Returns the minimum of the available values.
+ *
+ * <p>The result is <code>NaN</code> if any of the values is <code>NaN</code>.</p>
+ * <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>
+ * <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.</p>
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Helper function that returns a new instance of {@link UnsupportedOperationException}.
+     * @return a new {@code UnsupportedOperationException} instance
+     */
+    static UnsupportedOperationException uoe() {
+        return new UnsupportedOperationException();
+    }
+
+    /**
+     * {@code Min} implementation that does not store the input value(s) processed so far.
+     */
+    private static final class StorelessMin extends Min {
+
+        /**Current min. */
+        private double min;
+
+        /**
+         * Create a StorelessMin instance.
+         */
+        StorelessMin() {
+            min = Double.POSITIVE_INFINITY;
+        }
+
+        /**
+         * Updates the state of Min statistic to reflect the addition of the new value.
+         * @param value  the new value.
+         */
+        @Override
+        public void accept(double value) {
+            min = Double.min(min, value);
+        }
+
+        @Override
+        public double getAsDouble() {
+            return min;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public <U extends DoubleStatisticAccumulator<Min>> void combine(U other) {
+            final Min otherMin = other.getDoubleStatistic();
+            accept(otherMin.getAsDouble());
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Min getDoubleStatistic() {
+            return this;
+        }
+    }
+
+    /**
+     * Immutable {@code Min} implementation.
+     */
+    private static final class ImmutableMin extends Min {
+
+        /**Min delegate. */
+        private final Min delegate;
+
+        /**
+         * Initializes the Min delegate.
+         * @param delegate  Min delegate
+         */
+        ImmutableMin(final Min delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public void accept(double value) {
+            throw uoe();
+        }
+
+        @Override
+        public double getAsDouble() {
+            return delegate.getAsDouble();
+        }
+
+        @Override
+        public <U extends DoubleStatisticAccumulator<Min>> void combine(U other) {
+            throw uoe();
+        }
+
+        @Override
+        public Min getDoubleStatistic() {
+            return this;
+        }
+    }
+
+    /**
+     * Creates a {@code Min} implementation which does not store the input value(s) it consumes.
+     *
+     * @return {@code Min} implementation that does not store the input value(s) processed.
+     */
+    public static Min createStoreless() {

Review Comment:
   I would change this to `create()`. Any future implementations can overload create with implementation choices.
   
   You should document the special case of the result value when no values have been added, i.e. that min=+inf.



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    public void testEmpty() {
+        Min min = Min.createStoreless();
+        Min immutable = Min.of();
+
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, immutable.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    public void testMin(double[] values, double expectedMin) {
+        Min stat = Min.createStoreless();
+        for (final double value: values) {
+            stat.accept(value);
+        }
+        double actualMin = stat.getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin, "min");
+        Assertions.assertEquals(expectedMin, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {Double.NaN, 3.14, Double.NaN, Double.NaN}, Double.NaN),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747)
+        );
+    }
+
+    @Test
+    public void testMinImmutable() {
+        Min stat = Min.of(1.0, 2.0, -1.0, 4.0);
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> stat.accept(3.14),
+                "cannot update an immutable instance");
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    public void testCombine(double[] first, double[] second, double expectedMin) {
+        Min firstMin = Min.createStoreless();
+        Min secondMin = Min.createStoreless();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expectedMin, firstMin.getAsDouble());
+
+        secondMin.combine(firstMin);

Review Comment:
   Note here that `firstMin` has been changed to be the min, irrespective of whether first or second contained the minimum. So the second combine tests that this value is passed on to secondMin. This is redundant if you set up your arguments to target all conditions with the first combine.
   
   Ideally you wish to test 3 conditions where one or other of the pair are the minimum:
   ```
   A(min) + B(not-min)
   A(not min) + B(min)
   A(min) + B(min)
   ```
   Plus:
   ```
   A(empty)+B(empty)
   A(not-empty)+B(empty)
   A(empty)+B(not-empty)
   ```
   You can also add arrays with NaNs in there.
   
   I would create the conditions in the stream of the arguments. Inside the test method then test a+b or b+a, with new objects for each combine, and test the RHS argument is unchanged by the combine.



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    public void testEmpty() {
+        Min min = Min.createStoreless();
+        Min immutable = Min.of();
+
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, immutable.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    public void testMin(double[] values, double expectedMin) {
+        Min stat = Min.createStoreless();
+        for (final double value: values) {
+            stat.accept(value);
+        }
+        double actualMin = stat.getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin, "min");
+        Assertions.assertEquals(expectedMin, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {Double.NaN, 3.14, Double.NaN, Double.NaN}, Double.NaN),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747)
+        );
+    }
+
+    @Test
+    public void testMinImmutable() {
+        Min stat = Min.of(1.0, 2.0, -1.0, 4.0);
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> stat.accept(3.14),
+                "cannot update an immutable instance");
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    public void testCombine(double[] first, double[] second, double expectedMin) {
+        Min firstMin = Min.createStoreless();
+        Min secondMin = Min.createStoreless();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expectedMin, firstMin.getAsDouble());
+
+        secondMin.combine(firstMin);
+        Assertions.assertEquals(expectedMin, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, new double[] {2.718}, 2.718),
+                Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 1.1, 333.333}, 1.1),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 23.45}, -2.0),
+                Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] {1.1}, -2023.79),
+                Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 2.718, -0.0}, -0.0),
+                Arguments.of(new double[] {0.0, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NEGATIVE_INFINITY, -0.0, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0, Double.NaN, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NaN, -0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    public void testParallelStream(double[] values, double expectedMin) {
+        double actualMin = Arrays.stream(values).parallel().collect(Min::createStoreless, Min::accept, Min::combine).getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin);
+
+        DoubleStream nanStream = DoubleStream.of(Double.NaN, Double.NaN, Double.NaN);

Review Comment:
   This test should be its own fixture since it does not use the test parameters.



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 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;
+
+import java.util.Arrays;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    public void testEmpty() {
+        Min min = Min.createStoreless();
+        Min immutable = Min.of();
+
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, immutable.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    public void testMin(double[] values, double expectedMin) {
+        Min stat = Min.createStoreless();
+        for (final double value: values) {
+            stat.accept(value);
+        }
+        double actualMin = stat.getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin, "min");
+        Assertions.assertEquals(expectedMin, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {Double.NaN, 3.14, Double.NaN, Double.NaN}, Double.NaN),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747)
+        );
+    }
+
+    @Test
+    public void testMinImmutable() {
+        Min stat = Min.of(1.0, 2.0, -1.0, 4.0);
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> stat.accept(3.14),
+                "cannot update an immutable instance");
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    public void testCombine(double[] first, double[] second, double expectedMin) {
+        Min firstMin = Min.createStoreless();
+        Min secondMin = Min.createStoreless();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expectedMin, firstMin.getAsDouble());
+
+        secondMin.combine(firstMin);
+        Assertions.assertEquals(expectedMin, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, new double[] {2.718}, 2.718),
+                Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 1.1, 333.333}, 1.1),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 23.45}, -2.0),
+                Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] {1.1}, -2023.79),
+                Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 2.718, -0.0}, -0.0),
+                Arguments.of(new double[] {0.0, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NEGATIVE_INFINITY, -0.0, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0, Double.NaN, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NaN, -0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    public void testParallelStream(double[] values, double expectedMin) {
+        double actualMin = Arrays.stream(values).parallel().collect(Min::createStoreless, Min::accept, Min::combine).getAsDouble();
+        Assertions.assertEquals(expectedMin, actualMin);
+
+        DoubleStream nanStream = DoubleStream.of(Double.NaN, Double.NaN, Double.NaN);
+        Assertions.assertTrue(Double.isNaN(nanStream.parallel().collect(Min::createStoreless, Min::accept, Min::combine).getAsDouble()));
+    }
+
+    static Stream<Arguments> testParallelStream() {
+        return Stream.of(
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                Arguments.of(new double[] {1.2, -34.56, 456.789, -5678.9012}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747),
+                Arguments.of(new double[] {-1d, 1d, Double.NaN}, Double.NaN),
+                Arguments.of(new double[] {+0.0d, -0.0d, 1.0, 3.14}, -0.0d),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0d, Double.NaN, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}, Double.NaN)
+        );
+    }
+
+    @Test
+    public void testCombineImmutable() {
+        Min immutable = Min.of(1.1, 22.22, -333.333);
+        Min mutable = Min.createStoreless();
+        mutable.combine(immutable);
+        Assertions.assertEquals(-333.333, mutable.getAsDouble());
+        Assertions.assertEquals(-333.333, immutable.getAsDouble());
+
+        Min mutable2 = Min.createStoreless();
+        mutable2.accept(-4444.4444);
+        mutable2.combine(immutable);
+        Assertions.assertEquals(-4444.4444, mutable2.getAsDouble());
+
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> immutable.combine(mutable),
+                "cannot update an immutable instance");
+    }
+
+    @Test
+    public void testSpecialValues() {
+        double[] testArray = {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
+        Min stat = Min.createStoreless();
+
+        stat.accept(testArray[0]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[1]);
+        Assertions.assertEquals(0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[2]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[3]);
+        Assertions.assertEquals(-0.0d, stat.getAsDouble());
+
+        stat.accept(testArray[4]);
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, stat.getAsDouble());
+
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, Min.of(testArray).getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    public void testNaNs(double[] values, double expectedMin) {
+        Min stat = Min.createStoreless();
+        for (final double value: values) {
+            stat.accept(value);
+        }
+        Assertions.assertEquals(expectedMin, stat.getAsDouble());
+
+        Assertions.assertTrue(Double.isNaN(Min.of(Double.NaN, Double.NaN, Double.NaN).getAsDouble()));

Review Comment:
   Move to a difference test fixture as it does not use the arguments.



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+/**
+ * Returns the minimum of the available values.
+ *
+ * <p>The result is <code>NaN</code> if any of the values is <code>NaN</code>.</p>
+ * <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>
+ * <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.</p>
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Helper function that returns a new instance of {@link UnsupportedOperationException}.
+     * @return a new {@code UnsupportedOperationException} instance
+     */
+    static UnsupportedOperationException uoe() {
+        return new UnsupportedOperationException();
+    }
+
+    /**
+     * {@code Min} implementation that does not store the input value(s) processed so far.
+     */
+    private static final class StorelessMin extends Min {
+
+        /**Current min. */
+        private double min;
+
+        /**
+         * Create a StorelessMin instance.
+         */
+        StorelessMin() {
+            min = Double.POSITIVE_INFINITY;
+        }
+
+        /**
+         * Updates the state of Min statistic to reflect the addition of the new value.
+         * @param value  the new value.
+         */
+        @Override
+        public void accept(double value) {
+            min = Double.min(min, value);
+        }
+
+        @Override
+        public double getAsDouble() {
+            return min;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public <U extends DoubleStatisticAccumulator<Min>> void combine(U other) {
+            final Min otherMin = other.getDoubleStatistic();
+            accept(otherMin.getAsDouble());
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Min getDoubleStatistic() {
+            return this;
+        }
+    }
+
+    /**
+     * Immutable {@code Min} implementation.
+     */
+    private static final class ImmutableMin extends Min {
+
+        /**Min delegate. */
+        private final Min delegate;
+
+        /**
+         * Initializes the Min delegate.
+         * @param delegate  Min delegate
+         */
+        ImmutableMin(final Min delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public void accept(double value) {
+            throw uoe();
+        }
+
+        @Override
+        public double getAsDouble() {
+            return delegate.getAsDouble();
+        }
+
+        @Override
+        public <U extends DoubleStatisticAccumulator<Min>> void combine(U other) {
+            throw uoe();
+        }
+
+        @Override
+        public Min getDoubleStatistic() {
+            return this;
+        }
+    }
+
+    /**
+     * Creates a {@code Min} implementation which does not store the input value(s) it consumes.
+     *
+     * @return {@code Min} implementation that does not store the input value(s) processed.
+     */
+    public static Min createStoreless() {
+        return new StorelessMin();
+    }
+
+    /**
+     * Returns an immutable {@code Min} object that has the minimum of all the input value(s).

Review Comment:
   Again, document the value if the input is an empty array.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] ani5rudh commented on a diff in pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "ani5rudh (via GitHub)" <gi...@apache.org>.
ani5rudh commented on code in PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#discussion_r1263952537


##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.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 org.apache.commons.rng.UniformRandomProvider;

Review Comment:
   Fixed!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] ani5rudh closed pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "ani5rudh (via GitHub)" <gi...@apache.org>.
ani5rudh closed pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.
URL: https://github.com/apache/commons-statistics/pull/46


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] kinow commented on a diff in pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "kinow (via GitHub)" <gi...@apache.org>.
kinow commented on code in PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#discussion_r1262903784


##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java:
##########
@@ -0,0 +1,42 @@
+package org.apache.commons.statistics.descriptive;

Review Comment:
   I was too slow to hit enter! :sweat_smile: 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] kinow commented on pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "kinow (via GitHub)" <gi...@apache.org>.
kinow commented on PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#issuecomment-1628607771

   Approved GH actions, will have a look at the code today, @ani5rudh . Thanks!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] aherbert commented on pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "aherbert (via GitHub)" <gi...@apache.org>.
aherbert commented on PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#issuecomment-1636697975

   I have fixed the build on master, please rebase.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] asfgit closed pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "asfgit (via GitHub)" <gi...@apache.org>.
asfgit closed pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.
URL: https://github.com/apache/commons-statistics/pull/46


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] aherbert commented on a diff in pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "aherbert (via GitHub)" <gi...@apache.org>.
aherbert commented on code in PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#discussion_r1263667839


##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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 org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expected) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actual = stat.getAsDouble();
+        Assertions.assertEquals(expected, actual, "min");
+        Assertions.assertEquals(expected, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {}, Double.POSITIVE_INFINITY),
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                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[] {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}, -0.0d),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.MIN_VALUE}, -0.0),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, -Double.MIN_VALUE}, -Double.MIN_VALUE),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.MIN_VALUE}, Double.NEGATIVE_INFINITY)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCombine(double[] first, double[] second, double expected) {
+        Min firstMin = Min.create();
+        Min secondMin = Min.create();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        double secondMinBeforeCombine = secondMin.getAsDouble();
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expected, firstMin.getAsDouble());
+        Assertions.assertEquals(secondMinBeforeCombine, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {}, new double[] {}, Double.POSITIVE_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}, 2.718),
+                Arguments.of(new double[] {-1, 0, 1}, new double[] {1.1, 2.2, 3.3}, -1),
+                Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 1.1, 333.333}, 1.1),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 23.45}, -2.0),
+                Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] {1.1}, -2023.79),
+                Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 2.718, -0.0}, -0.0),
+                Arguments.of(new double[] {0.0, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NEGATIVE_INFINITY, -0.0, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0, Double.NaN, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NaN, -0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = "testMin")
+    void testParallelStream(double[] values, double expected) {
+        double actual = Arrays.stream(values).parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble();
+        Assertions.assertEquals(expected, actual);
+    }
+
+    @Test
+    void testNanParallelStream() {

Review Comment:
   This test is redundant if you add an array of NaN to `static Stream<Arguments> testMin()`



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java:
##########
@@ -0,0 +1,60 @@
+/*
+ * 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 org.apache.commons.rng.UniformRandomProvider;
+
+/**
+ * Helper class for tests in {@code o.a.c.s.descriptive} module.
+ */
+public final class TestHelper {
+
+    /** Class contains only static methods. */
+    private TestHelper() {}
+
+    /**
+     * Shuffles the entries of the given array.
+     *
+     * <p>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>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).
+     */

Review Comment:
   No return tag



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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 org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expected) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actual = stat.getAsDouble();
+        Assertions.assertEquals(expected, actual, "min");
+        Assertions.assertEquals(expected, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {}, Double.POSITIVE_INFINITY),
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                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[] {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}, -0.0d),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.MIN_VALUE}, -0.0),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, -Double.MIN_VALUE}, -Double.MIN_VALUE),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.MIN_VALUE}, Double.NEGATIVE_INFINITY)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCombine(double[] first, double[] second, double expected) {
+        Min firstMin = Min.create();
+        Min secondMin = Min.create();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        double secondMinBeforeCombine = secondMin.getAsDouble();
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expected, firstMin.getAsDouble());
+        Assertions.assertEquals(secondMinBeforeCombine, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {}, new double[] {}, Double.POSITIVE_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}, 2.718),
+                Arguments.of(new double[] {-1, 0, 1}, new double[] {1.1, 2.2, 3.3}, -1),
+                Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 1.1, 333.333}, 1.1),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 23.45}, -2.0),
+                Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] {1.1}, -2023.79),
+                Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 2.718, -0.0}, -0.0),
+                Arguments.of(new double[] {0.0, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NEGATIVE_INFINITY, -0.0, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0, Double.NaN, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NaN, -0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = "testMin")
+    void testParallelStream(double[] values, double expected) {
+        double actual = Arrays.stream(values).parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble();
+        Assertions.assertEquals(expected, actual);
+    }
+
+    @Test
+    void testNanParallelStream() {
+        DoubleStream nanStream = DoubleStream.of(Double.NaN, Double.NaN, Double.NaN);
+        Assertions.assertTrue(Double.isNaN(nanStream.parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble()));
+    }
+
+    @Test
+    void testIncrement() {
+        // Test the min after each incremental update
+        // First parameter of testArray is the value that would be added
+        // Second parameter of testArray is the min we expect after adding the value
+        double[][] testArray = {
+            {1729.22, 1729.22},
+            {153.75, 153.75},
+            {370.371, 153.75},
+            {0.0, 0.0},
+            {+0.0, 0.0},
+            {-0.0, -0.0},
+            {Double.POSITIVE_INFINITY, -0.0},
+            {Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY},
+            {Double.MIN_VALUE, Double.NEGATIVE_INFINITY}
+        };
+
+        Min stat = Min.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};
+        Min stat = Min.create();
+        for (final double x : testArray) {
+            stat.accept(x);
+            Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+        }
+
+        Assertions.assertTrue(Double.isNaN(Min.of(Double.NaN, Double.NaN, Double.NaN).getAsDouble()));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testArrayOfArrays(double[][] input, double expected) {
+        double actual = Arrays.stream(input)
+                .map(Min::of)
+                .reduce(Min::combine)
+                .map(DoubleSupplier::getAsDouble)
+                .orElseThrow(RuntimeException::new);
+
+        Assertions.assertEquals(expected, actual);
+    }
+
+    static Stream<Arguments> testArrayOfArrays() {
+        return Stream.of(
+            Arguments.of(new double[][] {{}, {}, {}}, Double.POSITIVE_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}}, -1.7),
+            Arguments.of(new double[][] {{1, 2}, {3, 4}}, 1),
+            Arguments.of(new double[][] {{+0.0, 2.0}, {1.0, -0.0, 3.14}}, -0.0),
+            Arguments.of(new double[][] {{+0.0, Double.NEGATIVE_INFINITY}, {-0.0, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}}, Double.NEGATIVE_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)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = "testMin")
+    void testMinRandomOrder(double[] values, double expected) {
+        UniformRandomProvider rng = RandomSource.SPLIT_MIX_64.create();

Review Comment:
   For reusability we could move this to `TestHelper`:
   ```java
   static UniformRandomProvider createRNG();
   ```
   This will provide a single point to change the RNG used by all tests using randomness.
   
   



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java:
##########
@@ -0,0 +1,60 @@
+/*
+ * 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 org.apache.commons.rng.UniformRandomProvider;
+
+/**
+ * Helper class for tests in {@code o.a.c.s.descriptive} module.
+ */
+public final class TestHelper {

Review Comment:
   No need to be public, same for the methods.



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/DoubleStatistic.java:
##########
@@ -0,0 +1,29 @@
+/*
+ * 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.DoubleConsumer;
+import java.util.function.DoubleSupplier;
+
+

Review Comment:
   Extra line here



##########
commons-statistics-descriptive/pom.xml:
##########
@@ -43,6 +43,19 @@
     </properties>
 
     <dependencies>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-rng-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-rng-sampling</artifactId>
+            <scope>test</scope>
+        </dependency>
+        

Review Comment:
   There is trailing whitespace in this line.



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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 org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expected) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actual = stat.getAsDouble();
+        Assertions.assertEquals(expected, actual, "min");
+        Assertions.assertEquals(expected, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {}, Double.POSITIVE_INFINITY),
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                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[] {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}, -0.0d),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.MIN_VALUE}, -0.0),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, -Double.MIN_VALUE}, -Double.MIN_VALUE),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.MIN_VALUE}, Double.NEGATIVE_INFINITY)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCombine(double[] first, double[] second, double expected) {
+        Min firstMin = Min.create();
+        Min secondMin = Min.create();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        double secondMinBeforeCombine = secondMin.getAsDouble();
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expected, firstMin.getAsDouble());
+        Assertions.assertEquals(secondMinBeforeCombine, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {}, new double[] {}, Double.POSITIVE_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}, 2.718),
+                Arguments.of(new double[] {-1, 0, 1}, new double[] {1.1, 2.2, 3.3}, -1),
+                Arguments.of(new double[] {3.14, 1.1, 22.22}, new double[] {2.718, 1.1, 333.333}, 1.1),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, new double[] {0.0, 23.45}, -2.0),
+                Arguments.of(new double[] {-2023.79, 11.11, 333.333}, new double[] {1.1}, -2023.79),
+                Arguments.of(new double[] {1.1, +0.0, 3.14}, new double[] {22.22, 2.718, -0.0}, -0.0),
+                Arguments.of(new double[] {0.0, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NEGATIVE_INFINITY, -0.0, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NEGATIVE_INFINITY),
+                Arguments.of(new double[] {0.0, Double.NaN, -Double.MIN_VALUE, Double.POSITIVE_INFINITY},
+                        new double[] {Double.NaN, -0.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.MIN_VALUE},
+                        Double.NaN)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource(value = "testMin")
+    void testParallelStream(double[] values, double expected) {
+        double actual = Arrays.stream(values).parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble();
+        Assertions.assertEquals(expected, actual);
+    }
+
+    @Test
+    void testNanParallelStream() {
+        DoubleStream nanStream = DoubleStream.of(Double.NaN, Double.NaN, Double.NaN);
+        Assertions.assertTrue(Double.isNaN(nanStream.parallel().collect(Min::create, Min::accept, Min::combine).getAsDouble()));
+    }
+
+    @Test
+    void testIncrement() {
+        // Test the min after each incremental update
+        // First parameter of testArray is the value that would be added
+        // Second parameter of testArray is the min we expect after adding the value
+        double[][] testArray = {
+            {1729.22, 1729.22},
+            {153.75, 153.75},
+            {370.371, 153.75},
+            {0.0, 0.0},
+            {+0.0, 0.0},
+            {-0.0, -0.0},
+            {Double.POSITIVE_INFINITY, -0.0},
+            {Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY},
+            {Double.MIN_VALUE, Double.NEGATIVE_INFINITY}
+        };
+
+        Min stat = Min.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};
+        Min stat = Min.create();
+        for (final double x : testArray) {
+            stat.accept(x);
+            Assertions.assertEquals(Double.NaN, stat.getAsDouble());
+        }
+
+        Assertions.assertTrue(Double.isNaN(Min.of(Double.NaN, Double.NaN, Double.NaN).getAsDouble()));

Review Comment:
   This assertion is redundant if you add an array of NaN to `static Stream<Arguments> testMin()`



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/DoubleStatistic.java:
##########
@@ -0,0 +1,29 @@
+/*
+ * 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.DoubleConsumer;
+import java.util.function.DoubleSupplier;
+
+
+/**
+ * Represents a state object for computing a single {@code Statistic} over {@code double} valued input(s).
+ *
+ * <p>Base interface implemented by all statistics.
+ */
+public interface DoubleStatistic extends DoubleConsumer, DoubleSupplier {
+}

Review Comment:
   I think this should have a comment. The comment used in Commons RNG for the composite sampler interfaces is:
   ```
       // Composite interface
   ```
   



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/package-info.java:
##########
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementations of univariate statistics.
+ */

Review Comment:
   Please add `@since 1.1` tags to all the public classes, interfaces and the package info.



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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 org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expected) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actual = stat.getAsDouble();
+        Assertions.assertEquals(expected, actual, "min");
+        Assertions.assertEquals(expected, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {}, Double.POSITIVE_INFINITY),

Review Comment:
   Can you change these to indent with 4 trailing spaces (not 8)



##########
commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Min.java:
##########
@@ -0,0 +1,137 @@
+/*
+ * 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 minimum 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>POSITIVE_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.
+ */
+public abstract class Min implements DoubleStatistic, DoubleStatisticAccumulator<Min> {
+
+    /**
+     * Create a Min instance.
+     */
+    Min() {
+        // No-op
+    }
+
+    /**
+     * Creates a {@code Min} 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#POSITIVE_INFINITY POSITIVE_INFINITY}
+     * if no values have been added.
+     *
+     * @return {@code Min} implementation.
+     */
+    public static Min create() {
+        return new StorelessMin();
+    }
+
+    /**
+     * Returns a {@code Min} instance that has the minimum 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#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @param values Values.
+     * @return {@code Min} instance.
+     */
+    public static Min of(double... values) {
+        final StorelessMin min = new StorelessMin();
+        Arrays.stream(values).forEach(min);
+        return min;
+    }
+
+    /**
+     * 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 minimum of all input values.
+     *
+     * <p>When no values have been added, the result is
+     * {@link Double#POSITIVE_INFINITY POSITIVE_INFINITY}.
+     *
+     * @return {@code Minimum} of all values seen so far.
+     */
+    @Override
+    public abstract double getAsDouble();
+
+    /** {@inheritDoc} */
+    @Override
+    public abstract Min combine(Min other);
+
+    /**
+     * {@code Min} implementation that does not store the input value(s) processed so far.
+     *
+     * <p>Uses JDK's {@link Math#min Math.min} as an underlying function
+     * to compute the {@code minimum}.
+     */
+    private static class StorelessMin extends Min {
+
+        /** Current min. */
+        private double min = Double.POSITIVE_INFINITY;
+
+        /** Creates an instance. */
+        StorelessMin() {

Review Comment:
   You can remove this constructor.



##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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 org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+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;
+
+import java.util.Arrays;
+import java.util.function.DoubleSupplier;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * Test for {@link Min}.
+ */
+final class MinTest {
+
+    @Test
+    void testEmpty() {
+        Min min = Min.create();
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, min.getAsDouble());
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testMin(double[] values, double expected) {
+        Min stat = Min.create();
+        Arrays.stream(values).forEach(stat);
+        double actual = stat.getAsDouble();
+        Assertions.assertEquals(expected, actual, "min");
+        Assertions.assertEquals(expected, Min.of(values).getAsDouble(), "min");
+    }
+
+    static Stream<Arguments> testMin() {
+        return Stream.of(
+                Arguments.of(new double[] {}, Double.POSITIVE_INFINITY),
+                Arguments.of(new double[] {3.14}, 3.14),
+                Arguments.of(new double[] {12.34, 56.78, -2.0}, -2.0),
+                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[] {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}, -0.0d),
+                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}, -5678.9012),
+                Arguments.of(new double[] {-23467824, 23648, 2368, 23749, -23424, -23492, -92397747}, -92397747),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.MIN_VALUE}, -0.0),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, -Double.MIN_VALUE}, -Double.MIN_VALUE),
+                Arguments.of(new double[] {0.0d, +0.0d, -0.0d, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.MIN_VALUE}, Double.NEGATIVE_INFINITY)
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testCombine(double[] first, double[] second, double expected) {
+        Min firstMin = Min.create();
+        Min secondMin = Min.create();
+
+        Arrays.stream(first).forEach(firstMin);
+        Arrays.stream(second).forEach(secondMin);
+
+        double secondMinBeforeCombine = secondMin.getAsDouble();
+        firstMin.combine(secondMin);
+        Assertions.assertEquals(expected, firstMin.getAsDouble());
+        Assertions.assertEquals(secondMinBeforeCombine, secondMin.getAsDouble());
+    }
+
+    static Stream<Arguments> testCombine() {
+        return Stream.of(
+                Arguments.of(new double[] {}, new double[] {}, Double.POSITIVE_INFINITY),

Review Comment:
   Can you change these to indent with 4 trailing spaces (not 8)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] aherbert commented on a diff in pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "aherbert (via GitHub)" <gi...@apache.org>.
aherbert commented on code in PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#discussion_r1263942513


##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/MinTest.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 org.apache.commons.rng.UniformRandomProvider;

Review Comment:
   I note that your IDE has chosen a specific order for the imports here. I believe all the other code in the Statistics project use the lexographical order for the imports, i.e. it does not put the java.* imports out of order, e.g. this:
   ```java
   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;
   ```
   Se if you can set up your development environment to use this order for this project.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] ani5rudh commented on a diff in pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "ani5rudh (via GitHub)" <gi...@apache.org>.
ani5rudh commented on code in PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#discussion_r1262905998


##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java:
##########
@@ -0,0 +1,42 @@
+package org.apache.commons.statistics.descriptive;

Review Comment:
   Sorry, I had missed it. I've fixed it in the next commit.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] aherbert commented on pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "aherbert (via GitHub)" <gi...@apache.org>.
aherbert commented on PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#issuecomment-1629131582

   The pom does have a default goal that is used by the GH action. The only difference is the GH action includes the examples module as well. So to run all the same checks as GH just type `mvn` from the project root.
   
   One other item to add is to document that the `Min` implementation returns the same order as that imposed by `Math#min`. 


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] kinow commented on a diff in pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "kinow (via GitHub)" <gi...@apache.org>.
kinow commented on code in PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#discussion_r1262888683


##########
commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java:
##########
@@ -0,0 +1,42 @@
+package org.apache.commons.statistics.descriptive;

Review Comment:
   Missing license?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] kinow commented on a diff in pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "kinow (via GitHub)" <gi...@apache.org>.
kinow commented on code in PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#discussion_r1264161636


##########
src/conf/checkstyle/checkstyle.xml:
##########
@@ -117,11 +117,15 @@
     <!-- Checks for imports -->
     <!-- See http://checkstyle.org/config_import.html -->
     <module name="AvoidStarImport" />
+    <module name="AvoidStaticImport"/>
     <module name="IllegalImport" /> <!-- defaults to sun.* packages -->
     <module name="RedundantImport" />
     <module name="UnusedImports">
       <property name="processJavadoc" value="false" />
     </module>
+    <module name="CustomImportOrder">
+      <property name="sortImportsInGroupAlphabetically" value="true"/>

Review Comment:
   :+1: 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] codecov-commenter commented on pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "codecov-commenter (via GitHub)" <gi...@apache.org>.
codecov-commenter commented on PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#issuecomment-1627488509

   ## [Codecov](https://app.codecov.io/gh/apache/commons-statistics/pull/46?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) Report
   > Merging [#46](https://app.codecov.io/gh/apache/commons-statistics/pull/46?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) (d41be79) into [master](https://app.codecov.io/gh/apache/commons-statistics/commit/763ba719c5972488c2fa76901f50faa5124440e5?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) (763ba71) will **not change** coverage.
   > The diff coverage is `n/a`.
   
   ```diff
   @@            Coverage Diff            @@
   ##             master      #46   +/-   ##
   =========================================
     Coverage     99.82%   99.82%           
     Complexity     1688     1688           
   =========================================
     Files            65       65           
     Lines          4472     4472           
     Branches        756      756           
   =========================================
     Hits           4464     4464           
     Misses            4        4           
     Partials          4        4           
   ```
   
   
   
   :mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] ani5rudh commented on pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "ani5rudh (via GitHub)" <gi...@apache.org>.
ani5rudh commented on PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#issuecomment-1636889908

   I tried to rebase onto `apache:master` but in the process, the commits were duplicated in this PR. I tried to remove those duplicate commits by resetting and force pushing. But, accidentally, all the commits were lost and this PR was closed automatically. Apologies for this mess up. 
   
   I was able to recover the commits by checking out to the last commit before all of this happened.  I force pushed the recovered commits to the branch on my forked repo and created a new PR. 
   
   Link to the new PR: https://github.com/apache/commons-statistics/pull/48  


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [commons-statistics] ani5rudh commented on pull request #46: [STATISTICS-71]: Add base interfaces for all statistic implementations.

Posted by "ani5rudh (via GitHub)" <gi...@apache.org>.
ani5rudh commented on PR #46:
URL: https://github.com/apache/commons-statistics/pull/46#issuecomment-1636089311

   Hello @aherbert, thanks for the review. I've updated the PR per your comments. 


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@commons.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org