001/*
002 * Java Genetic Algorithm Library (jenetics-7.2.0).
003 * Copyright (c) 2007-2023 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         *
093         * <pre>{@code
094         * final Stream<SomeObject> stream = ...
095         * final DoubleSummary summary = stream
096         *     .collect(toDoubleSummary(v -> v.doubleValue()));
097         * }</pre>
098         *
099         * @param mapper a mapping function to apply to each element
100         * @param <T> the type of the input elements
101         * @return a {@code Collector} implementing the summary-statistics reduction
102         * @throws java.lang.NullPointerException if the given {@code mapper} is
103         *         {@code null}
104         */
105        public static <T> Collector<T, ?, DoubleSummary>
106        toDoubleSummary(final ToDoubleFunction<? super T> mapper) {
107                requireNonNull(mapper);
108                return Collector.of(
109                        DoubleSummaryStatistics::new,
110                        (a, b) -> a.accept(mapper.applyAsDouble(b)),
111                        (a, b) -> {a.combine(b); return a;},
112                        DoubleSummary::of
113                );
114        }
115
116
117        /* *************************************************************************
118         * Some static helper methods.
119         **************************************************************************/
120
121        /**
122         * Return the minimum value of the given double array.
123         *
124         * @since 4.0
125         *
126         * @param values the double array.
127         * @return the minimum value or {@link Double#NaN} if the given array is
128         *         empty.
129         * @throws NullPointerException if the given array is {@code null}.
130         */
131        public static double min(final double[] values) {
132                double min = NaN;
133                if (values.length > 0) {
134                        min = values[0];
135
136                        for (double value : values) {
137                                if (value < min) {
138                                        min = value;
139                                }
140                        }
141                }
142
143                return min;
144        }
145
146        /**
147         * Return the maximum value of the given double array.
148         *
149         * @since 4.0
150         *
151         * @param values the double array.
152         * @return the maximum value or {@link Double#NaN} if the given array is
153         *         empty.
154         * @throws NullPointerException if the given array is {@code null}.
155         */
156        public static double max(final double[] values) {
157                double max = NaN;
158                if (values.length > 0) {
159                        max = values[0];
160
161                        for (double value : values) {
162                                if (value > max) {
163                                        max = value;
164                                }
165                        }
166                }
167
168                return max;
169        }
170
171        /**
172         * Return the sum of the given double array.
173         *
174         * @since 4.0
175         *
176         * @param values the values to sum up.
177         * @return the sum of the given {@code values}.
178         * @throws NullPointerException if the given array is {@code null}.
179         */
180        public static double sum(final double[] values) {
181                return DoubleAdder.sum(values);
182        }
183
184        /**
185         * Returns a double describing the arithmetic mean of the values, or
186         * {@link Double#NaN} if the {@code values} array is empty.
187         *
188         * @since 4.0
189         *
190         * @param values the values to calculate the mean of
191         * @return the arithmetic mean of the given {@code values} or
192         *         {@link Double#NaN} if the {@code values} array is empty
193         * @throws NullPointerException if the given array is {@code null}.
194         */
195        public static double mean(final double[] values) {
196                return values.length > 0 ? sum(values)/values.length : NaN;
197        }
198
199}