001/*
002 * Java Genetic Algorithm Library (jenetics-8.0.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.util.Objects.requireNonNull;
023
024import java.io.Serial;
025import java.io.Serializable;
026import java.util.function.ToDoubleFunction;
027import java.util.stream.Collector;
028
029/**
030 * <i>Value</i> objects which contains statistical moments.
031 *
032 * @see io.jenetics.stat.DoubleMomentStatistics
033 *
034 * @param count the count of values recorded
035 * @param min the minimum value recorded, or {@link Double#POSITIVE_INFINITY} if
036 *            no values have been recorded.
037 * @param max the maximum value recorded, or {@link Double#NEGATIVE_INFINITY} if
038 *            no values have been recorded
039 * @param sum the sum of values recorded, or zero if no values have been
040 *            recorded
041 * @param mean the arithmetic mean of values recorded, or zero if no values have
042 *            been recorded
043 * @param variance the variance of values recorded, or {@link Double#NaN} if no
044 *            values have been recorded
045 * @param skewness the <a href="https://en.wikipedia.org/wiki/Skewness">Skewness</a>
046 *        of values recorded, or {@link Double#NaN} if less than two values have
047 *        been recorded
048 * @param kurtosis the <a href="https://en.wikipedia.org/wiki/Kurtosis">Kurtosis</a>
049 *        of values recorded, or {@link Double#NaN} if less than four values
050 *        have been recorded
051 *
052 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
053 * @since 3.0
054 * @version 7.0
055 */
056public record DoubleMoments(
057        long count,
058        double min,
059        double max,
060        double sum,
061        double mean,
062        double variance,
063        double skewness,
064        double kurtosis
065)
066        implements Serializable
067{
068
069        @Serial
070        private static final long serialVersionUID = 2L;
071
072        @Override
073        public String toString() {
074                return String.format(
075                        "DoubleMoments[N=%d, ∧=%s, ∨=%s, Σ=%s, μ=%s, s²=%s, S=%s, K=%s]",
076                        count(), min(), max(), sum(),
077                        mean(), variance(), skewness(), kurtosis()
078                );
079        }
080
081        /**
082         * Return a new value object of the statistical moments, currently
083         * represented by the {@code statistics} object.
084         *
085         * @param statistics the creating (mutable) statistics class
086         * @return the statistical moments
087         */
088        public static DoubleMoments of(final DoubleMomentStatistics statistics) {
089                return new DoubleMoments(
090                        statistics.count(),
091                        statistics.min(),
092                        statistics.max(),
093                        statistics.sum(),
094                        statistics.mean(),
095                        statistics.variance(),
096                        statistics.skewness(),
097                        statistics.kurtosis()
098                );
099        }
100
101        /**
102         * Return a {@code Collector} which returns moments-statistics for the
103         * resulting values.
104         *
105         * {@snippet lang="java":
106         * final Stream<Double> stream = null; // @replace substring='null' replacement="..."
107         * final DoubleMoments moments = stream.collect(toDoubleMoments());
108         * }
109         *
110         * @since 4.1
111         *
112         * @param <N> the type of the input elements
113         * @return a {@code Collector} implementing the moments-statistics reduction
114         */
115        public static <N extends Number> Collector<N, ?, DoubleMoments>
116        toDoubleMoments() {
117                return toDoubleMoments(Number::doubleValue);
118        }
119
120        /**
121         * Return a {@code Collector} which applies a double-producing mapping
122         * function to each input element, and returns moments-statistics for the
123         * resulting values.
124         *
125         * {@snippet lang="java":
126         * final Stream<SomeObject> stream = null; // @replace substring='null' replacement="..."
127         * final DoubleMoments moments = stream
128         *     .collect(toDoubleMoments(v -> v.doubleValue()));
129         * }
130         *
131         * @param mapper a mapping function to apply to each element
132         * @param <T> the type of the input elements
133         * @return a {@code Collector} implementing the moments-statistics reduction
134         * @throws java.lang.NullPointerException if the given {@code mapper} is
135         *         {@code null}
136         */
137        public static <T> Collector<T, ?, DoubleMoments>
138        toDoubleMoments(final ToDoubleFunction<? super T> mapper) {
139                requireNonNull(mapper);
140                return Collector.of(
141                        DoubleMomentStatistics::new,
142                        (a, b) -> a.accept(mapper.applyAsDouble(b)),
143                        DoubleMomentStatistics::combine,
144                        DoubleMoments::of
145                );
146        }
147
148}