001/*
002 * Java Genetic Algorithm Library (jenetics-8.1.0).
003 * Copyright (c) 2007-2024 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020package io.jenetics.stat;
021
022import static java.lang.Double.NaN;
023import static java.util.Objects.requireNonNull;
024
025import java.io.Serial;
026import java.io.Serializable;
027import java.util.DoubleSummaryStatistics;
028import java.util.function.ToDoubleFunction;
029import java.util.stream.Collector;
030
031import io.jenetics.internal.math.DoubleAdder;
032
033/**
034 * <i>Value</i> objects which contains statistical summary information.
035 *
036 * @see java.util.DoubleSummaryStatistics
037 *
038 * @param count the count of values recorded
039 * @param min the minimum value recorded, or {@link Double#POSITIVE_INFINITY} if
040 *        no values have been recorded
041 * @param max the maximum value recorded, or {@link Double#NEGATIVE_INFINITY} if
042 *            no values have been recorded
043 * @param sum the sum of values recorded, or zero if no values have been recorded
044 * @param mean the arithmetic mean of values recorded, or zero if no values have
045 *            been recorded
046 *
047 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
048 * @since 3.0
049 * @version 7.0
050 */
051public record DoubleSummary(
052        long count,
053        double min,
054        double max,
055        double sum,
056        double mean
057)
058        implements Serializable
059{
060        @Serial
061        private static final long serialVersionUID = 2L;
062
063        @Override
064        public String toString() {
065                return String.format(
066                        "DoubleSummary[N=%d, ∧=%s, ∨=%s, Σ=%s, μ=%s]",
067                        count(), min(), max(), sum(), mean()
068                );
069        }
070
071        /**
072         * Return a new value object of the statistical summary, currently
073         * represented by the {@code statistics} object.
074         *
075         * @param statistics the creating (mutable) statistics class
076         * @return the statistical moments
077         */
078        public static DoubleSummary of(final DoubleSummaryStatistics statistics) {
079                return new DoubleSummary(
080                        statistics.getCount(),
081                        statistics.getMin(),
082                        statistics.getMax(),
083                        statistics.getSum(),
084                        statistics.getAverage()
085                );
086        }
087
088        /**
089         * Return a {@code Collector} which applies a double-producing mapping
090         * function to each input element, and return summary-statistics for the
091         * resulting values.
092         * {@snippet lang="java":
093         * final Stream<SomeObject> stream = null; // @replace substring='null' replacement="..."
094         * final DoubleSummary summary = stream
095         *     .collect(toDoubleSummary(v -> v.doubleValue()));
096         * }
097         *
098         * @param mapper a mapping function to apply to each element
099         * @param <T> the type of the input elements
100         * @return a {@code Collector} implementing the summary-statistics reduction
101         * @throws java.lang.NullPointerException if the given {@code mapper} is
102         *         {@code null}
103         */
104        public static <T> Collector<T, ?, DoubleSummary>
105        toDoubleSummary(final ToDoubleFunction<? super T> mapper) {
106                requireNonNull(mapper);
107                return Collector.of(
108                        DoubleSummaryStatistics::new,
109                        (a, b) -> a.accept(mapper.applyAsDouble(b)),
110                        (a, b) -> {a.combine(b); return a;},
111                        DoubleSummary::of
112                );
113        }
114
115
116        /* *************************************************************************
117         * Some static helper methods.
118         **************************************************************************/
119
120        /**
121         * Return the minimum value of the given double array.
122         *
123         * @since 4.0
124         *
125         * @param values the double array.
126         * @return the minimum value or {@link Double#NaN} if the given array is
127         *         empty.
128         * @throws NullPointerException if the given array is {@code null}.
129         */
130        public static double min(final double[] values) {
131                double min = NaN;
132                if (values.length > 0) {
133                        min = values[0];
134
135                        for (double value : values) {
136                                if (value < min) {
137                                        min = value;
138                                }
139                        }
140                }
141
142                return min;
143        }
144
145        /**
146         * Return the maximum value of the given double array.
147         *
148         * @since 4.0
149         *
150         * @param values the double array.
151         * @return the maximum value or {@link Double#NaN} if the given array is
152         *         empty.
153         * @throws NullPointerException if the given array is {@code null}.
154         */
155        public static double max(final double[] values) {
156                double max = NaN;
157                if (values.length > 0) {
158                        max = values[0];
159
160                        for (double value : values) {
161                                if (value > max) {
162                                        max = value;
163                                }
164                        }
165                }
166
167                return max;
168        }
169
170        /**
171         * Return the sum of the given double array.
172         *
173         * @since 4.0
174         *
175         * @param values the values to sum up.
176         * @return the sum of the given {@code values}.
177         * @throws NullPointerException if the given array is {@code null}.
178         */
179        public static double sum(final double[] values) {
180                return DoubleAdder.sum(values);
181        }
182
183        /**
184         * Returns a double describing the arithmetic mean of the values, or
185         * {@link Double#NaN} if the {@code values} array is empty.
186         *
187         * @since 4.0
188         *
189         * @param values the values to calculate the mean of
190         * @return the arithmetic mean of the given {@code values} or
191         *         {@link Double#NaN} if the {@code values} array is empty
192         * @throws NullPointerException if the given array is {@code null}.
193         */
194        public static double mean(final double[] values) {
195                return values.length > 0 ? sum(values)/values.length : NaN;
196        }
197
198}