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.lang.String.format;
023import static java.util.Objects.requireNonNull;
024
025import java.util.Comparator;
026import java.util.Objects;
027import java.util.function.Consumer;
028import java.util.function.Function;
029import java.util.stream.Collector;
030import java.util.stream.Stream;
031
032import io.jenetics.util.Streams;
033
034/**
035 * This <i>consumer</i> class is used for calculating the min and max value
036 * according to the given {@code Comparator}.
037 * <p>
038 * This class is designed to work with (though does not require) streams. For
039 * example, you can compute minimum and maximum values with:
040 * {@snippet lang="java":
041 * final Stream<Integer> stream = null; // @replace substring='null' replacement="..."
042 * final MinMax<Integer> minMax = stream.collect(
043 *         MinMax::of,
044 *         MinMax::accept,
045 *         MinMax::combine
046 *     );
047 * }
048 *
049 * @implNote
050 * This implementation is not thread safe. However, it is safe to use on a
051 * parallel stream, because the parallel implementation of
052 * {@link java.util.stream.Stream#collect Stream.collect()}provides the
053 * necessary partitioning, isolation, and merging of results for safe and
054 * efficient parallel execution.
055 *
056 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
057 * @since 3.0
058 * @version 6.0
059 */
060public final class MinMax<C> implements Consumer<C> {
061
062        private final Comparator<? super C> _comparator;
063
064        private C _min;
065        private C _max;
066        private long _count = 0L;
067
068        private MinMax(final Comparator<? super C> comparator) {
069                _comparator = requireNonNull(comparator);
070        }
071
072        /**
073         * Accept the element for min-max calculation.
074         *
075         * @param object the element to use for min-max calculation
076         */
077        @Override
078        public void accept(final C object) {
079                _min = min(_comparator, _min, object);
080                _max = max(_comparator, _max, object);
081                ++_count;
082        }
083
084        /**
085         * Combine two {@code MinMax} objects.
086         *
087         * @param other the other {@code MinMax} object to combine
088         * @return {@code this}
089         * @throws java.lang.NullPointerException if the {@code other} object is
090         *         {@code null}.
091         */
092        public MinMax<C> combine(final MinMax<C> other) {
093                _min = min(_comparator, _min, other._min);
094                _max = max(_comparator, _max, other._max);
095                _count += other._count;
096
097                return this;
098        }
099
100        /**
101         * Returns the count of values recorded.
102         *
103         * @return the count of recorded values
104         */
105        public long count() {
106                return _count;
107        }
108
109        /**
110         * Return the current minimal object or {@code null} if no element has been
111         * accepted yet.
112         *
113         * @return the current minimal object
114         */
115        public C min() {
116                return _min;
117        }
118
119        /**
120         * Return the current maximal object or {@code null} if no element has been
121         * accepted yet.
122         *
123         * @return the current maximal object
124         */
125        public C max() {
126                return _max;
127        }
128
129        /**
130         * Compares the state of two {@code LongMomentStatistics} objects. This is
131         * a replacement for the {@link #equals(Object)} which is not advisable to
132         * implement for this mutable object. If two objects have the same state, it
133         * has still the same state when updated with the same value.
134         * {@snippet lang="java":
135         * final MinMax<Long> mm1 = null; // @replace substring='null' replacement="..."
136         * final MinMax<Long> mm2 = null; // @replace substring='null' replacement="..."
137         *
138         * if (mm1.sameState(mm2)) {
139         *     final long value = random.nextInt(1_000_000);
140         *     mm1.accept(value);
141         *     mm2.accept(value);
142         *
143         *     assert mm1.sameState(mm2);
144         *     assert mm2.sameState(mm1);
145         *     assert mm1.sameState(mm1);
146         * }
147         * }
148         *
149         * @since 3.7
150         *
151         * @param other the other object for the test
152         * @return {@code true} the {@code this} and the {@code other} objects have
153         *         the same state, {@code false} otherwise
154         */
155        public boolean sameState(final MinMax<C> other) {
156                return this == other ||
157                        Objects.equals(_min, other._min) &&
158                        Objects.equals(_max, other._max);
159        }
160
161        @Override
162        public String toString() {
163                return format("MinMax[count=%d, min=%s, max=%s]", _count, _min, _max);
164        }
165
166        /* *************************************************************************
167         *  Some static helper methods.
168         * ************************************************************************/
169
170        /**
171         * Return the minimum of two values, according the given comparator.
172         * {@code null} values are allowed.
173         *
174         * @param comp the comparator used for determining the min value
175         * @param a the first value to compare
176         * @param b the second value to compare
177         * @param <T> the type of the compared objects
178         * @return the minimum value, or {@code null} if both values are {@code null}.
179         *         If only one value is {@code null}, the non {@code null} values is
180         *         returned.
181         */
182        public static <T> T
183        min(final Comparator<? super T> comp, final T a, final T b) {
184                return a != null ? b != null ? comp.compare(a, b) <= 0 ? a : b : a : b;
185        }
186
187        /**
188         * Return the maximum of two values, according the given comparator.
189         * {@code null} values are allowed.
190         *
191         * @param comp the comparator used for determining the max value
192         * @param a the first value to compare
193         * @param b the second value to compare
194         * @param <T> the type of the compared objects
195         * @return the maximum value, or {@code null} if both values are {@code null}.
196         *         If only one value is {@code null}, the non {@code null} values is
197         *         returned.
198         */
199        public static <T> T
200        max(final Comparator<? super T> comp, final T a, final T b) {
201                return a != null ? b != null ? comp.compare(a, b) >= 0 ? a : b : a : b;
202        }
203
204
205        /* *************************************************************************
206         *  Some static factory methods.
207         * ************************************************************************/
208
209        /**
210         * Return a {@code Collector} which calculates the minimum and maximum value.
211         * The given {@code comparator} is used for comparing two objects.
212         *
213         * {@snippet lang="java":
214         * final Comparator<SomeObject> comparator = null; // @replace substring='null' replacement="..."
215         * final Stream<SomeObject> stream = null; // @replace substring='null' replacement="..."
216         * final MinMax<SomeObject> moments = stream
217         *     .collect(doubleMoments.toMinMax(comparator));
218         * }
219         *
220         * @param comparator the {@code Comparator} to use
221         * @param <T> the type of the input elements
222         * @return a {@code Collector} implementing the min-max reduction
223         * @throws java.lang.NullPointerException if the given {@code mapper} is
224         *         {@code null}
225         */
226        public static <T> Collector<T, ?, MinMax<T>>
227        toMinMax(final Comparator<? super T> comparator) {
228                requireNonNull(comparator);
229                return Collector.of(
230                        () -> MinMax.of(comparator),
231                        MinMax::accept,
232                        MinMax::combine
233                );
234        }
235
236        /**
237         * Return a {@code Collector} which calculates the minimum and maximum value.
238         * The <i>reducing</i> objects must be comparable.
239         * <p>
240         * {@snippet lang="java":
241         * final Stream<SomeObject> stream = null; // @replace substring='null' replacement="..."
242         * final MinMax<SomeObject> moments = stream
243         *     .collect(doubleMoments.toMinMax(comparator));
244         * }
245         *
246         * @param <C> the type of the input elements
247         * @return a {@code Collector} implementing the min-max reduction
248         * @throws java.lang.NullPointerException if the given {@code mapper} is
249         *         {@code null}
250         */
251        public static <C extends Comparable<? super C>>
252        Collector<C, ?, MinMax<C>> toMinMax() {
253                return toMinMax(Comparator.naturalOrder());
254        }
255
256        /**
257         * Create a new {@code MinMax} <i>consumer</i> with the given
258         * {@link java.util.Comparator}.
259         *
260         * @param comparator the comparator used for comparing two elements
261         * @param <T> the element type
262         * @return a new {@code MinMax} <i>consumer</i>
263         * @throws java.lang.NullPointerException if the {@code comparator} is
264         *         {@code null}.
265         */
266        public static <T> MinMax<T> of(final Comparator<? super T> comparator) {
267                return new MinMax<>(comparator);
268        }
269
270        /**
271         * Create a new {@code MinMax} <i>consumer</i>.
272         *
273         * @param <C> the element type
274         * @return a new {@code MinMax} <i>consumer</i>
275         */
276        public static <C extends Comparable<? super C>> MinMax<C> of() {
277                return of(Comparator.naturalOrder());
278        }
279
280
281        /* *************************************************************************
282         *  Some "flat" mapper functions.
283         * ************************************************************************/
284
285        /**
286         * Return a new flat-mapper function, which guarantees a strictly increasing
287         * stream, from an arbitrarily ordered source stream. Note that this
288         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
289         * order</em> elements.
290         * <p>
291         * {@snippet lang="java":
292         * final ISeq<Integer> values = new Random().ints(0, 100)
293         *     .boxed()
294         *     .limit(100)
295         *     .flatMap(MinMax.toStrictlyIncreasing())
296         *     .collect(ISeq.toISeq());
297         *
298         * System.out.println(values);
299         * // [6,47,65,78,96,96,99]
300         * }
301         *
302         * @since 5.0
303         *
304         * @param <C> the comparable type
305         * @return a new flat-mapper function
306         */
307        public static <C extends Comparable<? super C>>
308        Function<C, Stream<C>> toStrictlyIncreasing() {
309                return Streams.toStrictlyIncreasing();
310        }
311
312        /**
313         * Return a new flat-mapper function, which guarantees a strictly decreasing
314         * stream, from an arbitrarily ordered source stream. Note that this
315         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
316         * order</em> elements.
317         * <p>
318         * {@snippet lang="java":
319         * final ISeq<Integer> values = new Random().ints(0, 100)
320         *     .boxed()
321         *     .limit(100)
322         *     .flatMap(MinMax.toStrictlyDecreasing())
323         *     .collect(ISeq.toISeq());
324         *
325         * System.out.println(values);
326         * // [45,32,15,12,3,1]
327         * }
328         *
329         * @since 5.0
330         *
331         * @param <C> the comparable type
332         * @return a new flat-mapper function
333         */
334        public static <C extends Comparable<? super C>>
335        Function<C, Stream<C>> toStrictlyDecreasing() {
336                return Streams.toStrictlyDecreasing();
337        }
338
339        /**
340         * Return a new flat-mapper function, which guarantees a strictly improving
341         * stream, from an arbitrarily ordered source stream. Note that this
342         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
343         * order</em> elements.
344         * <p>
345         * {@snippet lang="java":
346         * final ISeq<Integer> values = new Random().ints(0, 100)
347         *     .boxed()
348         *     .limit(100)
349         *     .flatMap(MinMax.toStrictlyImproving(Comparator.naturalOrder()))
350         *     .collect(ISeq.toISeq());
351         *
352         * System.out.println(values);
353         * // [6,47,65,78,96,96,99]
354         * }
355         *
356         * @since 6.0
357         *
358         * @see #toStrictlyIncreasing()
359         * @see #toStrictlyDecreasing()
360         *
361         * @param <T> the element type
362         * @param comparator the comparator used for testing the elements
363         * @return a new flat-mapper function
364         */
365        public static <T> Function<T, Stream<T>>
366        toStrictlyImproving(final Comparator<? super T> comparator) {
367                return Streams.toStrictlyImproving(comparator);
368        }
369
370}