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) <= 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 *
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 }
|