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