001/*
002 * Java Genetic Algorithm Library (jenetics-8.1.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         * {@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)
292         *     .boxed()
293         *     .limit(100)
294         *     .flatMap(MinMax.toStrictlyIncreasing())
295         *     .collect(ISeq.toISeq());
296         *
297         * System.out.println(values);
298         * // [6,47,65,78,96,96,99]
299         * }
300         *
301         * @since 5.0
302         *
303         * @param <C> the comparable type
304         * @return a new flat-mapper function
305         */
306        public static <C extends Comparable<? super C>>
307        Function<C, Stream<C>> toStrictlyIncreasing() {
308                return Streams.toStrictlyIncreasing();
309        }
310
311        /**
312         * Return a new flat-mapper function, which guarantees a strictly decreasing
313         * stream, from an arbitrarily ordered source stream. Note that this
314         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
315         * order</em> elements.
316         * <p>
317         * {@snippet lang="java":
318         * final ISeq<Integer> values = new Random().ints(0, 100)
319         *     .boxed()
320         *     .limit(100)
321         *     .flatMap(MinMax.toStrictlyDecreasing())
322         *     .collect(ISeq.toISeq());
323         *
324         * System.out.println(values);
325         * // [45,32,15,12,3,1]
326         * }
327         *
328         * @since 5.0
329         *
330         * @param <C> the comparable type
331         * @return a new flat-mapper function
332         */
333        public static <C extends Comparable<? super C>>
334        Function<C, Stream<C>> toStrictlyDecreasing() {
335                return Streams.toStrictlyDecreasing();
336        }
337
338        /**
339         * Return a new flat-mapper function, which guarantees a strictly improving
340         * stream, from an arbitrarily ordered source stream. Note that this
341         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
342         * order</em> elements.
343         * <p>
344         * {@snippet lang="java":
345         * final ISeq<Integer> values = new Random().ints(0, 100)
346         *     .boxed()
347         *     .limit(100)
348         *     .flatMap(MinMax.toStrictlyImproving(Comparator.naturalOrder()))
349         *     .collect(ISeq.toISeq());
350         *
351         * System.out.println(values);
352         * // [6,47,65,78,96,96,99]
353         * }
354         *
355         * @since 6.0
356         *
357         * @see #toStrictlyIncreasing()
358         * @see #toStrictlyDecreasing()
359         *
360         * @param <T> the element type
361         * @param comparator the comparator used for testing the elements
362         * @return a new flat-mapper function
363         */
364        public static <T> Function<T, Stream<T>>
365        toStrictlyImproving(final Comparator<? super T> comparator) {
366                return Streams.toStrictlyImproving(comparator);
367        }
368
369}