001/*
002 * Java Genetic Algorithm Library (jenetics-8.2.0).
003 * Copyright (c) 2007-2025 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 Objects.equals(_min, other._min) &&
157                        Objects.equals(_max, other._max);
158        }
159
160        @Override
161        public String toString() {
162                return format("MinMax[count=%d, min=%s, max=%s]", _count, _min, _max);
163        }
164
165        /* *************************************************************************
166         *  Some static helper methods.
167         * ************************************************************************/
168
169        /**
170         * Return the minimum of two values, according the given comparator.
171         * {@code null} values are allowed.
172         *
173         * @param comp the comparator used for determining the min value
174         * @param a the first value to compare
175         * @param b the second value to compare
176         * @param <T> the type of the compared objects
177         * @return the minimum value, or {@code null} if both values are {@code null}.
178         *         If only one value is {@code null}, the non {@code null} values is
179         *         returned.
180         */
181        public static <T> T
182        min(final Comparator<? super T> comp, final T a, final T b) {
183                return a != null ? b != null ? comp.compare(a, b) <= 0 ? a : b : a : b;
184        }
185
186        /**
187         * Return the maximum of two values, according the given comparator.
188         * {@code null} values are allowed.
189         *
190         * @param comp the comparator used for determining the max value
191         * @param a the first value to compare
192         * @param b the second value to compare
193         * @param <T> the type of the compared objects
194         * @return the maximum value, or {@code null} if both values are {@code null}.
195         *         If only one value is {@code null}, the non {@code null} values is
196         *         returned.
197         */
198        public static <T> T
199        max(final Comparator<? super T> comp, final T a, final T b) {
200                return a != null ? b != null ? comp.compare(a, b) >= 0 ? a : b : a : b;
201        }
202
203
204        /* *************************************************************************
205         *  Some static factory methods.
206         * ************************************************************************/
207
208        /**
209         * Return a {@code Collector} which calculates the minimum and maximum value.
210         * The given {@code comparator} is used for comparing two objects.
211         * {@snippet lang="java":
212         * final Comparator<SomeObject> comparator = null; // @replace substring='null' replacement="..."
213         * final Stream<SomeObject> stream = null; // @replace substring='null' replacement="..."
214         * final MinMax<SomeObject> moments = stream
215         *     .collect(doubleMoments.toMinMax(comparator));
216         * }
217         *
218         * @param comparator the {@code Comparator} to use
219         * @param <T> the type of the input elements
220         * @return a {@code Collector} implementing the min-max reduction
221         * @throws java.lang.NullPointerException if the given {@code mapper} is
222         *         {@code null}
223         */
224        public static <T> Collector<T, ?, MinMax<T>>
225        toMinMax(final Comparator<? super T> comparator) {
226                requireNonNull(comparator);
227                return Collector.of(
228                        () -> MinMax.of(comparator),
229                        MinMax::accept,
230                        MinMax::combine
231                );
232        }
233
234        /**
235         * Return a {@code Collector} which calculates the minimum and maximum value.
236         * The <i>reducing</i> objects must be comparable.
237         * <p>
238         * {@snippet lang="java":
239         * final Stream<SomeObject> stream = null; // @replace substring='null' replacement="..."
240         * final MinMax<SomeObject> moments = stream
241         *     .collect(doubleMoments.toMinMax(comparator));
242         * }
243         *
244         * @param <C> the type of the input elements
245         * @return a {@code Collector} implementing the min-max reduction
246         * @throws java.lang.NullPointerException if the given {@code mapper} is
247         *         {@code null}
248         */
249        public static <C extends Comparable<? super C>>
250        Collector<C, ?, MinMax<C>> toMinMax() {
251                return toMinMax(Comparator.naturalOrder());
252        }
253
254        /**
255         * Create a new {@code MinMax} <i>consumer</i> with the given
256         * {@link java.util.Comparator}.
257         *
258         * @param comparator the comparator used for comparing two elements
259         * @param <T> the element type
260         * @return a new {@code MinMax} <i>consumer</i>
261         * @throws java.lang.NullPointerException if the {@code comparator} is
262         *         {@code null}.
263         */
264        public static <T> MinMax<T> of(final Comparator<? super T> comparator) {
265                return new MinMax<>(comparator);
266        }
267
268        /**
269         * Create a new {@code MinMax} <i>consumer</i>.
270         *
271         * @param <C> the element type
272         * @return a new {@code MinMax} <i>consumer</i>
273         */
274        public static <C extends Comparable<? super C>> MinMax<C> of() {
275                return of(Comparator.naturalOrder());
276        }
277
278
279        /* *************************************************************************
280         *  Some "flat" mapper functions.
281         * ************************************************************************/
282
283        /**
284         * Return a new flat-mapper function, which guarantees a strictly increasing
285         * stream, from an arbitrarily ordered source stream. Note that this
286         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
287         * order</em> elements.
288         * <p>
289         * {@snippet lang="java":
290         * final ISeq<Integer> values = new Random().ints(0, 100)
291         *     .boxed()
292         *     .limit(100)
293         *     .flatMap(MinMax.toStrictlyIncreasing())
294         *     .collect(ISeq.toISeq());
295         *
296         * System.out.println(values);
297         * // [6,47,65,78,96,96,99]
298         * }
299         *
300         * @since 5.0
301         *
302         * @param <C> the comparable type
303         * @return a new flat-mapper function
304         */
305        public static <C extends Comparable<? super C>>
306        Function<C, Stream<C>> toStrictlyIncreasing() {
307                return Streams.toStrictlyIncreasing();
308        }
309
310        /**
311         * Return a new flat-mapper function, which guarantees a strictly decreasing
312         * stream, from an arbitrarily ordered source stream. Note that this
313         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
314         * order</em> elements.
315         * <p>
316         * {@snippet lang="java":
317         * final ISeq<Integer> values = new Random().ints(0, 100)
318         *     .boxed()
319         *     .limit(100)
320         *     .flatMap(MinMax.toStrictlyDecreasing())
321         *     .collect(ISeq.toISeq());
322         *
323         * System.out.println(values);
324         * // [45,32,15,12,3,1]
325         * }
326         *
327         * @since 5.0
328         *
329         * @param <C> the comparable type
330         * @return a new flat-mapper function
331         */
332        public static <C extends Comparable<? super C>>
333        Function<C, Stream<C>> toStrictlyDecreasing() {
334                return Streams.toStrictlyDecreasing();
335        }
336
337        /**
338         * Return a new flat-mapper function, which guarantees a strictly improving
339         * stream, from an arbitrarily ordered source stream. Note that this
340         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
341         * order</em> elements.
342         * <p>
343         * {@snippet lang="java":
344         * final ISeq<Integer> values = new Random().ints(0, 100)
345         *     .boxed()
346         *     .limit(100)
347         *     .flatMap(MinMax.toStrictlyImproving(Comparator.naturalOrder()))
348         *     .collect(ISeq.toISeq());
349         *
350         * System.out.println(values);
351         * // [6,47,65,78,96,96,99]
352         * }
353         *
354         * @since 6.0
355         *
356         * @see #toStrictlyIncreasing()
357         * @see #toStrictlyDecreasing()
358         *
359         * @param <T> the element type
360         * @param comparator the comparator used for testing the elements
361         * @return a new flat-mapper function
362         */
363        public static <T> Function<T, Stream<T>>
364        toStrictlyImproving(final Comparator<? super T> comparator) {
365                return Streams.toStrictlyImproving(comparator);
366        }
367
368}