001 /*
002 * Java Genetic Algorithm Library (jenetics-5.1.0).
003 * Copyright (c) 2007-2019 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 3.7
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 * Return the current minimal object or {@code null} if no element has been
101 * accepted yet.
102 *
103 * @return the current minimal object
104 */
105 public C getMin() {
106 return _min;
107 }
108
109 /**
110 * Return the current maximal object or {@code null} if no element has been
111 * accepted yet.
112 *
113 * @return the current maximal object
114 */
115 public C getMax() {
116 return _max;
117 }
118
119 /**
120 * Returns the count of values recorded.
121 *
122 * @return the count of recorded values
123 */
124 public long getCount() {
125 return _count;
126 }
127
128 /**
129 * Compares the state of two {@code LongMomentStatistics} objects. This is
130 * a replacement for the {@link #equals(Object)} which is not advisable to
131 * implement for this mutable object. If two object have the same state, it
132 * has still the same state when updated with the same value.
133 * <pre>{@code
134 * final MinMax mm1 = ...;
135 * final MinMax mm2 = ...;
136 *
137 * if (mm1.sameState(mm2)) {
138 * final long value = random.nextInt(1_000_000);
139 * mm1.accept(value);
140 * mm2.accept(value);
141 *
142 * assert mm1.sameState(mm2);
143 * assert mm2.sameState(mm1);
144 * assert mm1.sameState(mm1);
145 * }
146 * }</pre>
147 *
148 * @since 3.7
149 *
150 * @param other the other object for the test
151 * @return {@code true} the {@code this} and the {@code other} objects have
152 * the same state, {@code false} otherwise
153 */
154 public boolean sameState(final MinMax<C> other) {
155 return this == other ||
156 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 *
212 * <pre>{@code
213 * final Comparator<SomeObject> comparator = ...
214 * final Stream<SomeObject> stream = ...
215 * final MinMax<SomeObject> moments = stream
216 * .collect(doubleMoments.toMinMax(comparator));
217 * }</pre>
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 *
239 * <pre>{@code
240 * final Stream<SomeObject> stream = ...
241 * final MinMax<SomeObject> moments = stream
242 * .collect(doubleMoments.toMinMax(comparator));
243 * }</pre>
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 * Return a new flat-mapper function, which guarantees a strictly increasing
281 * stream, from an arbitrarily ordered source stream. Note that this
282 * function doesn't sort the stream. It <em>just</em> skips the <em>out of
283 * order</em> elements.
284 *
285 * <pre>{@code
286 * final ISeq<Integer> values = new Random().ints(0, 100)
287 * .boxed()
288 * .limit(100)
289 * .flatMap(MinMax.toStrictlyIncreasing())
290 * .collect(ISeq.toISeq());
291 *
292 * System.out.println(values);
293 * // [6,47,65,78,96,96,99]
294 * }</pre>
295 *
296 * @since 5.0
297 *
298 * @param <C> the comparable type
299 * @return a new flat-mapper function
300 */
301 public static <C extends Comparable<? super C>>
302 Function<C, Stream<C>> toStrictlyIncreasing() {
303 return toStrictly(MinMax::max);
304 }
305
306 /**
307 * Return a new flat-mapper function, which guarantees a strictly decreasing
308 * stream, from an arbitrarily ordered source stream. Note that this
309 * function doesn't sort the stream. It <em>just</em> skips the <em>out of
310 * order</em> elements.
311 *
312 * <pre>{@code
313 * final ISeq<Integer> values = new Random().ints(0, 100)
314 * .boxed()
315 * .limit(100)
316 * .flatMap(MinMax.toStrictlyDecreasing())
317 * .collect(ISeq.toISeq());
318 *
319 * System.out.println(values);
320 * // [45,32,15,12,3,1]
321 * }</pre>
322 *
323 * @since 5.0
324 *
325 * @param <C> the comparable type
326 * @return a new flat-mapper function
327 */
328 public static <C extends Comparable<? super C>>
329 Function<C, Stream<C>> toStrictlyDecreasing() {
330 return toStrictly(MinMax::min);
331 }
332
333 private static <C>
334 Function<C, Stream<C>> toStrictly(final BinaryOperator<C> comp) {
335 return new Function<C, Stream<C>>() {
336 private C _best;
337
338 @Override
339 public Stream<C> apply(final C result) {
340 final C best = comp.apply(_best, result);
341
342 final Stream<C> stream = best == _best
343 ? Stream.empty()
344 : Stream.of(best);
345
346 _best = best;
347
348 return stream;
349 }
350 };
351 }
352
353 private static <T extends Comparable<? super T>> T max(final T a, final T b) {
354 if (a == null && b == null) return null;
355 if (a == null) return b;
356 if (b == null) return a;
357 return a.compareTo(b) >= 0 ? a : b;
358 }
359
360 private static <T extends Comparable<? super T>> T min(final T a, final T b) {
361 if (a == null && b == null) return null;
362 if (a == null) return b;
363 if (b == null) return a;
364 return a.compareTo(b) <= 0 ? a : b;
365 }
366
367 }
|