MinMax.java
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  */
020 package io.jenetics.stat;
021 
022 import static java.lang.String.format;
023 import static java.util.Objects.requireNonNull;
024 
025 import java.util.Comparator;
026 import java.util.Objects;
027 import java.util.function.Consumer;
028 import java.util.function.Function;
029 import java.util.stream.Collector;
030 import java.util.stream.Stream;
031 
032 import 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  */
060 public 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<= ? 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>= ? 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 }