001/*
002 * Java Genetic Algorithm Library (jenetics-9.1.0).
003 * Copyright (c) 2007-2026 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.Gatherer;
031import java.util.stream.Stream;
032
033import io.jenetics.util.Streams;
034
035/**
036 * This <i>consumer</i> class is used for calculating the min and max value
037 * according to the given {@code Comparator}.
038 * <p>
039 * This class is designed to work with (though does not require) streams. For
040 * example, you can compute minimum and maximum values with:
041 * {@snippet lang="java":
042 * final Stream<Integer> stream = null; // @replace substring='null' replacement="..."
043 * final MinMax<Integer> minMax = stream.collect(
044 *         MinMax::of,
045 *         MinMax::accept,
046 *         MinMax::combine
047 *     );
048 * }
049 *
050 * @implNote
051 * This implementation is not thread safe. However, it is safe to use on a
052 * parallel stream, because the parallel implementation of
053 * {@link java.util.stream.Stream#collect Stream.collect()}provides the
054 * necessary partitioning, isolation, and merging of results for safe and
055 * efficient parallel execution.
056 *
057 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
058 * @since 3.0
059 * @version 6.0
060 */
061public final class MinMax<C> implements Consumer<C> {
062
063        private final Comparator<? super C> _comparator;
064
065        private C _min;
066        private C _max;
067        private long _count = 0L;
068
069        private MinMax(final Comparator<? super C> comparator) {
070                _comparator = requireNonNull(comparator);
071        }
072
073        /**
074         * Accept the element for min-max calculation.
075         *
076         * @param object the element to use for min-max calculation
077         */
078        @Override
079        public void accept(final C object) {
080                _min = min(_comparator, _min, object);
081                _max = max(_comparator, _max, object);
082                ++_count;
083        }
084
085        /**
086         * Combine two {@code MinMax} objects.
087         *
088         * @param other the other {@code MinMax} object to combine
089         * @return {@code this}
090         * @throws java.lang.NullPointerException if the {@code other} object is
091         *         {@code null}.
092         */
093        public MinMax<C> combine(final MinMax<C> other) {
094                _min = min(_comparator, _min, other._min);
095                _max = max(_comparator, _max, other._max);
096                _count += other._count;
097
098                return this;
099        }
100
101        /**
102         * Returns the count of values recorded.
103         *
104         * @return the count of recorded values
105         */
106        public long count() {
107                return _count;
108        }
109
110        /**
111         * Return the current minimal object or {@code null} if no element has been
112         * accepted yet.
113         *
114         * @return the current minimal object
115         */
116        public C min() {
117                return _min;
118        }
119
120        /**
121         * Return the current maximal object or {@code null} if no element has been
122         * accepted yet.
123         *
124         * @return the current maximal object
125         */
126        public C max() {
127                return _max;
128        }
129
130        /**
131         * Compares the state of two {@code LongMomentStatistics} objects. This is
132         * a replacement for the {@link #equals(Object)} which is not advisable to
133         * implement for this mutable object. If two objects have the same state, it
134         * has still the same state when updated with the same value.
135         * {@snippet lang="java":
136         * final MinMax<Long> mm1 = null; // @replace substring='null' replacement="..."
137         * final MinMax<Long> mm2 = null; // @replace substring='null' replacement="..."
138         *
139         * if (mm1.sameState(mm2)) {
140         *     final long value = random.nextInt(1_000_000);
141         *     mm1.accept(value);
142         *     mm2.accept(value);
143         *
144         *     assert mm1.sameState(mm2);
145         *     assert mm2.sameState(mm1);
146         *     assert mm1.sameState(mm1);
147         * }
148         * }
149         *
150         * @since 3.7
151         *
152         * @param other the other object for the test
153         * @return {@code true} the {@code this} and the {@code other} objects have
154         *         the same state, {@code false} otherwise
155         */
156        public boolean sameState(final MinMax<C> other) {
157                return 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         * {@snippet lang="java":
213         * final Comparator<SomeObject> comparator = null; // @replace substring='null' replacement="..."
214         * final Stream<SomeObject> stream = null; // @replace substring='null' replacement="..."
215         * final MinMax<SomeObject> moments = stream
216         *     .collect(doubleMoments.toMinMax(comparator));
217         * }
218         *
219         * @param comparator the {@code Comparator} to use
220         * @param <T> the type of the input elements
221         * @return a {@code Collector} implementing the min-max reduction
222         * @throws java.lang.NullPointerException if the given {@code mapper} is
223         *         {@code null}
224         */
225        public static <T> Collector<T, ?, MinMax<T>>
226        toMinMax(final Comparator<? super T> comparator) {
227                requireNonNull(comparator);
228                return Collector.of(
229                        () -> MinMax.of(comparator),
230                        MinMax::accept,
231                        MinMax::combine
232                );
233        }
234
235        /**
236         * Return a {@code Collector} which calculates the minimum and maximum value.
237         * The <i>reducing</i> objects must be comparable.
238         * <p>
239         * {@snippet lang="java":
240         * final Stream<SomeObject> stream = null; // @replace substring='null' replacement="..."
241         * final MinMax<SomeObject> moments = stream
242         *     .collect(doubleMoments.toMinMax(comparator));
243         * }
244         *
245         * @param <C> the type of the input elements
246         * @return a {@code Collector} implementing the min-max reduction
247         * @throws java.lang.NullPointerException if the given {@code mapper} is
248         *         {@code null}
249         */
250        public static <C extends Comparable<? super C>>
251        Collector<C, ?, MinMax<C>> toMinMax() {
252                return toMinMax(Comparator.naturalOrder());
253        }
254
255        /**
256         * Create a new {@code MinMax} <i>consumer</i> with the given
257         * {@link java.util.Comparator}.
258         *
259         * @param comparator the comparator used for comparing two elements
260         * @param <T> the element type
261         * @return a new {@code MinMax} <i>consumer</i>
262         * @throws java.lang.NullPointerException if the {@code comparator} is
263         *         {@code null}.
264         */
265        public static <T> MinMax<T> of(final Comparator<? super T> comparator) {
266                return new MinMax<>(comparator);
267        }
268
269        /**
270         * Create a new {@code MinMax} <i>consumer</i>.
271         *
272         * @param <C> the element type
273         * @return a new {@code MinMax} <i>consumer</i>
274         */
275        public static <C extends Comparable<? super C>> MinMax<C> of() {
276                return of(Comparator.naturalOrder());
277        }
278
279
280        /* *************************************************************************
281         *  Some "flat" mapper functions.
282         * ************************************************************************/
283
284        /**
285         * Return a new flat-mapper function, which guarantees a strictly increasing
286         * stream, from an arbitrarily ordered source stream. Note that this
287         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
288         * order</em> elements.
289         * <p>
290         * {@snippet lang="java":
291         * final ISeq<Integer> values = new Random().ints(0, 100).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         * @deprecated Used {@link #strictlyIncreasing()} instead.
305         */
306        @SuppressWarnings("removal")
307        @Deprecated(forRemoval = true, since = "9.1")
308        public static <C extends Comparable<? super C>>
309        Function<C, Stream<C>> toStrictlyIncreasing() {
310                return Streams.toStrictlyIncreasing();
311        }
312
313        /**
314         * Return a new gatherer, which guarantees a strictly increasing stream, from
315         * an arbitrarily ordered source stream. Note that this gatherer doesn't sort
316         * the stream. It <em>just</em> skips the <em>out of order</em> elements.
317         * <p>
318         * {@snippet lang="java":
319         * final ISeq<Integer> values = new Random().ints(0, 100).boxed()
320         *     .limit(100)
321         *     .gather(MinMax.strictlyIncreasing())
322         *     .collect(ISeq.toISeq());
323         *
324         * System.out.println(values);
325         * // [6,47,65,78,96,96,99]
326         * }
327         * @since 9.1
328         *
329         * @param <C> the comparable type
330         * @return a new flat-mapper function
331         */
332        public static <C extends Comparable<? super C>>
333        Gatherer<C, ?, C> strictlyIncreasing() {
334                return Streams.strictlyIncreasing();
335        }
336
337        /**
338         * Return a new gatherer, which guarantees a strictly decreasin stream, from
339         * an arbitrarily ordered source stream. Note that this gatherer doesn't sort
340         * the stream. It <em>just</em> skips the <em>out of order</em> elements.
341         *
342         * <pre>{@code
343         *     +----9--8--9--5--6--6--2--9----|
344         *        strictlyDecreasing()
345         *     +----9--8-----5--------2-------|
346         * }</pre>
347         *
348         * {@snippet lang="java":
349         * final ISeq<Integer> values = new Random().ints(0, 100)
350         *     .boxed()
351         *     .limit(100)
352         *     .gather(Streams.strictlyDecreasing())
353         *     .collect(ISeq.toISeq());
354         *
355         * System.out.println(values);
356         * // [45,32,15,12,3,1]
357         * }
358         * @since 9.1
359         *
360         * @param <C> the comparable type
361         * @return a new flat-mapper function
362         */
363        public static <C extends Comparable<? super C>>
364        Gatherer<C, ?, C> strictlyDecreasing() {
365                return Streams.strictlyDecreasing();
366        }
367
368        /**
369         * Return a new flat-mapper function, which guarantees a strictly decreasing
370         * stream, from an arbitrarily ordered source stream. Note that this
371         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
372         * order</em> elements.
373         * <p>
374         * {@snippet lang="java":
375         * final ISeq<Integer> values = new Random().ints(0, 100).boxed()
376         *     .limit(100)
377         *     .flatMap(MinMax.toStrictlyDecreasing())
378         *     .collect(ISeq.toISeq());
379         *
380         * System.out.println(values);
381         * // [45,32,15,12,3,1]
382         * }
383         *
384         * @since 5.0
385         *
386         * @param <C> the comparable type
387         * @return a new flat-mapper function
388         * @deprecated Used {@link #strictlyDecreasing()} instead.
389         */
390        @SuppressWarnings("removal")
391        @Deprecated(forRemoval = true, since = "9.1")
392        public static <C extends Comparable<? super C>>
393        Function<C, Stream<C>> toStrictlyDecreasing() {
394                return Streams.toStrictlyDecreasing();
395        }
396
397        /**
398         * Return a new flat-mapper function, which guarantees a strictly improving
399         * stream, from an arbitrarily ordered source stream. Note that this
400         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
401         * order</em> elements.
402         * <p>
403         * {@snippet lang="java":
404         * final ISeq<Integer> values = new Random().ints(0, 100).boxed()
405         *     .limit(100)
406         *     .flatMap(MinMax.toStrictlyImproving(Comparator.naturalOrder()))
407         *     .collect(ISeq.toISeq());
408         *
409         * System.out.println(values);
410         * // [6,47,65,78,96,96,99]
411         * }
412         *
413         * @since 6.0
414         *
415         * @see #toStrictlyIncreasing()
416         * @see #toStrictlyDecreasing()
417         *
418         * @param <T> the element type
419         * @param comparator the comparator used for testing the elements
420         * @return a new flat-mapper function
421         * @deprecated Use {@link #strictlyImproving(Comparator)} instead.
422         */
423        @SuppressWarnings("removal")
424        @Deprecated(forRemoval = true, since = "9.1")
425        public static <T> Function<T, Stream<T>>
426        toStrictlyImproving(final Comparator<? super T> comparator) {
427                return Streams.toStrictlyImproving(comparator);
428        }
429
430        /**
431         * Return a new flat-mapper function, which guarantees a strictly improving
432         * stream, from an arbitrarily ordered source stream. Note that this
433         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
434         * order</em> elements.
435         * <p>
436         * {@snippet lang="java":
437         * final ISeq<Integer> values = new Random().ints(0, 100).boxed()
438         *     .limit(100)
439         *     .gather(MinMax.strictlyImproving(Comparator.naturalOrder()))
440         *     .collect(ISeq.toISeq());
441         *
442         * System.out.println(values);
443         * // [6,47,65,78,96,96,99]
444         * }
445         *
446         * @since 9.1
447         *
448         * @see #strictlyIncreasing()
449         * @see #toStrictlyDecreasing()
450         *
451         * @param <T> the element type
452         * @param comparator the comparator used for testing the elements
453         * @return a new flat-mapper function
454         */
455        public static <T> Gatherer<T, ?, T>
456        strictlyImproving(final Comparator<? super T> comparator) {
457                return Streams.strictlyImproving(comparator);
458        }
459
460}