001/* 002 * Java Genetic Algorithm Library (jenetics-9.1.0). 003 * Copyright (c) 2007-2026 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.Gatherer; 031import java.util.stream.Stream; 032 033import io.jenetics.util.Streams; 034 035/** 036 * This <i>consumer</i> class is used for calculating the min and max value 037 * according to the given {@code Comparator}. 038 * <p> 039 * This class is designed to work with (though does not require) streams. For 040 * example, you can compute minimum and maximum values with: 041 * {@snippet lang="java": 042 * final Stream<Integer> stream = null; // @replace substring='null' replacement="..." 043 * final MinMax<Integer> minMax = stream.collect( 044 * MinMax::of, 045 * MinMax::accept, 046 * MinMax::combine 047 * ); 048 * } 049 * 050 * @implNote 051 * This implementation is not thread safe. However, it is safe to use on a 052 * parallel stream, because the parallel implementation of 053 * {@link java.util.stream.Stream#collect Stream.collect()}provides the 054 * necessary partitioning, isolation, and merging of results for safe and 055 * efficient parallel execution. 056 * 057 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 058 * @since 3.0 059 * @version 6.0 060 */ 061public final class MinMax<C> implements Consumer<C> { 062 063 private final Comparator<? super C> _comparator; 064 065 private C _min; 066 private C _max; 067 private long _count = 0L; 068 069 private MinMax(final Comparator<? super C> comparator) { 070 _comparator = requireNonNull(comparator); 071 } 072 073 /** 074 * Accept the element for min-max calculation. 075 * 076 * @param object the element to use for min-max calculation 077 */ 078 @Override 079 public void accept(final C object) { 080 _min = min(_comparator, _min, object); 081 _max = max(_comparator, _max, object); 082 ++_count; 083 } 084 085 /** 086 * Combine two {@code MinMax} objects. 087 * 088 * @param other the other {@code MinMax} object to combine 089 * @return {@code this} 090 * @throws java.lang.NullPointerException if the {@code other} object is 091 * {@code null}. 092 */ 093 public MinMax<C> combine(final MinMax<C> other) { 094 _min = min(_comparator, _min, other._min); 095 _max = max(_comparator, _max, other._max); 096 _count += other._count; 097 098 return this; 099 } 100 101 /** 102 * Returns the count of values recorded. 103 * 104 * @return the count of recorded values 105 */ 106 public long count() { 107 return _count; 108 } 109 110 /** 111 * Return the current minimal object or {@code null} if no element has been 112 * accepted yet. 113 * 114 * @return the current minimal object 115 */ 116 public C min() { 117 return _min; 118 } 119 120 /** 121 * Return the current maximal object or {@code null} if no element has been 122 * accepted yet. 123 * 124 * @return the current maximal object 125 */ 126 public C max() { 127 return _max; 128 } 129 130 /** 131 * Compares the state of two {@code LongMomentStatistics} objects. This is 132 * a replacement for the {@link #equals(Object)} which is not advisable to 133 * implement for this mutable object. If two objects have the same state, it 134 * has still the same state when updated with the same value. 135 * {@snippet lang="java": 136 * final MinMax<Long> mm1 = null; // @replace substring='null' replacement="..." 137 * final MinMax<Long> mm2 = null; // @replace substring='null' replacement="..." 138 * 139 * if (mm1.sameState(mm2)) { 140 * final long value = random.nextInt(1_000_000); 141 * mm1.accept(value); 142 * mm2.accept(value); 143 * 144 * assert mm1.sameState(mm2); 145 * assert mm2.sameState(mm1); 146 * assert mm1.sameState(mm1); 147 * } 148 * } 149 * 150 * @since 3.7 151 * 152 * @param other the other object for the test 153 * @return {@code true} the {@code this} and the {@code other} objects have 154 * the same state, {@code false} otherwise 155 */ 156 public boolean sameState(final MinMax<C> other) { 157 return 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).boxed() 292 * .limit(100) 293 * .flatMap(MinMax.toStrictlyIncreasing()) 294 * .collect(ISeq.toISeq()); 295 * 296 * System.out.println(values); 297 * // [6,47,65,78,96,96,99] 298 * } 299 * 300 * @since 5.0 301 * 302 * @param <C> the comparable type 303 * @return a new flat-mapper function 304 * @deprecated Used {@link #strictlyIncreasing()} instead. 305 */ 306 @SuppressWarnings("removal") 307 @Deprecated(forRemoval = true, since = "9.1") 308 public static <C extends Comparable<? super C>> 309 Function<C, Stream<C>> toStrictlyIncreasing() { 310 return Streams.toStrictlyIncreasing(); 311 } 312 313 /** 314 * Return a new gatherer, which guarantees a strictly increasing stream, from 315 * an arbitrarily ordered source stream. Note that this gatherer doesn't sort 316 * the stream. It <em>just</em> skips the <em>out of order</em> elements. 317 * <p> 318 * {@snippet lang="java": 319 * final ISeq<Integer> values = new Random().ints(0, 100).boxed() 320 * .limit(100) 321 * .gather(MinMax.strictlyIncreasing()) 322 * .collect(ISeq.toISeq()); 323 * 324 * System.out.println(values); 325 * // [6,47,65,78,96,96,99] 326 * } 327 * @since 9.1 328 * 329 * @param <C> the comparable type 330 * @return a new flat-mapper function 331 */ 332 public static <C extends Comparable<? super C>> 333 Gatherer<C, ?, C> strictlyIncreasing() { 334 return Streams.strictlyIncreasing(); 335 } 336 337 /** 338 * Return a new gatherer, which guarantees a strictly decreasin stream, from 339 * an arbitrarily ordered source stream. Note that this gatherer doesn't sort 340 * the stream. It <em>just</em> skips the <em>out of order</em> elements. 341 * 342 * <pre>{@code 343 * +----9--8--9--5--6--6--2--9----| 344 * strictlyDecreasing() 345 * +----9--8-----5--------2-------| 346 * }</pre> 347 * 348 * {@snippet lang="java": 349 * final ISeq<Integer> values = new Random().ints(0, 100) 350 * .boxed() 351 * .limit(100) 352 * .gather(Streams.strictlyDecreasing()) 353 * .collect(ISeq.toISeq()); 354 * 355 * System.out.println(values); 356 * // [45,32,15,12,3,1] 357 * } 358 * @since 9.1 359 * 360 * @param <C> the comparable type 361 * @return a new flat-mapper function 362 */ 363 public static <C extends Comparable<? super C>> 364 Gatherer<C, ?, C> strictlyDecreasing() { 365 return Streams.strictlyDecreasing(); 366 } 367 368 /** 369 * Return a new flat-mapper function, which guarantees a strictly decreasing 370 * stream, from an arbitrarily ordered source stream. Note that this 371 * function doesn't sort the stream. It <em>just</em> skips the <em>out of 372 * order</em> elements. 373 * <p> 374 * {@snippet lang="java": 375 * final ISeq<Integer> values = new Random().ints(0, 100).boxed() 376 * .limit(100) 377 * .flatMap(MinMax.toStrictlyDecreasing()) 378 * .collect(ISeq.toISeq()); 379 * 380 * System.out.println(values); 381 * // [45,32,15,12,3,1] 382 * } 383 * 384 * @since 5.0 385 * 386 * @param <C> the comparable type 387 * @return a new flat-mapper function 388 * @deprecated Used {@link #strictlyDecreasing()} instead. 389 */ 390 @SuppressWarnings("removal") 391 @Deprecated(forRemoval = true, since = "9.1") 392 public static <C extends Comparable<? super C>> 393 Function<C, Stream<C>> toStrictlyDecreasing() { 394 return Streams.toStrictlyDecreasing(); 395 } 396 397 /** 398 * Return a new flat-mapper function, which guarantees a strictly improving 399 * stream, from an arbitrarily ordered source stream. Note that this 400 * function doesn't sort the stream. It <em>just</em> skips the <em>out of 401 * order</em> elements. 402 * <p> 403 * {@snippet lang="java": 404 * final ISeq<Integer> values = new Random().ints(0, 100).boxed() 405 * .limit(100) 406 * .flatMap(MinMax.toStrictlyImproving(Comparator.naturalOrder())) 407 * .collect(ISeq.toISeq()); 408 * 409 * System.out.println(values); 410 * // [6,47,65,78,96,96,99] 411 * } 412 * 413 * @since 6.0 414 * 415 * @see #toStrictlyIncreasing() 416 * @see #toStrictlyDecreasing() 417 * 418 * @param <T> the element type 419 * @param comparator the comparator used for testing the elements 420 * @return a new flat-mapper function 421 * @deprecated Use {@link #strictlyImproving(Comparator)} instead. 422 */ 423 @SuppressWarnings("removal") 424 @Deprecated(forRemoval = true, since = "9.1") 425 public static <T> Function<T, Stream<T>> 426 toStrictlyImproving(final Comparator<? super T> comparator) { 427 return Streams.toStrictlyImproving(comparator); 428 } 429 430 /** 431 * Return a new flat-mapper function, which guarantees a strictly improving 432 * stream, from an arbitrarily ordered source stream. Note that this 433 * function doesn't sort the stream. It <em>just</em> skips the <em>out of 434 * order</em> elements. 435 * <p> 436 * {@snippet lang="java": 437 * final ISeq<Integer> values = new Random().ints(0, 100).boxed() 438 * .limit(100) 439 * .gather(MinMax.strictlyImproving(Comparator.naturalOrder())) 440 * .collect(ISeq.toISeq()); 441 * 442 * System.out.println(values); 443 * // [6,47,65,78,96,96,99] 444 * } 445 * 446 * @since 9.1 447 * 448 * @see #strictlyIncreasing() 449 * @see #toStrictlyDecreasing() 450 * 451 * @param <T> the element type 452 * @param comparator the comparator used for testing the elements 453 * @return a new flat-mapper function 454 */ 455 public static <T> Gatherer<T, ?, T> 456 strictlyImproving(final Comparator<? super T> comparator) { 457 return Streams.strictlyImproving(comparator); 458 } 459 460}