001/* 002 * Java Genetic Algorithm Library (jenetics-8.2.0). 003 * Copyright (c) 2007-2025 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.Stream; 031 032import 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 */ 060public 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 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 * {@snippet lang="java": 212 * final Comparator<SomeObject> comparator = null; // @replace substring='null' replacement="..." 213 * final Stream<SomeObject> stream = null; // @replace substring='null' replacement="..." 214 * final MinMax<SomeObject> moments = stream 215 * .collect(doubleMoments.toMinMax(comparator)); 216 * } 217 * 218 * @param comparator the {@code Comparator} to use 219 * @param <T> the type of the input elements 220 * @return a {@code Collector} implementing the min-max reduction 221 * @throws java.lang.NullPointerException if the given {@code mapper} is 222 * {@code null} 223 */ 224 public static <T> Collector<T, ?, MinMax<T>> 225 toMinMax(final Comparator<? super T> comparator) { 226 requireNonNull(comparator); 227 return Collector.of( 228 () -> MinMax.of(comparator), 229 MinMax::accept, 230 MinMax::combine 231 ); 232 } 233 234 /** 235 * Return a {@code Collector} which calculates the minimum and maximum value. 236 * The <i>reducing</i> objects must be comparable. 237 * <p> 238 * {@snippet lang="java": 239 * final Stream<SomeObject> stream = null; // @replace substring='null' replacement="..." 240 * final MinMax<SomeObject> moments = stream 241 * .collect(doubleMoments.toMinMax(comparator)); 242 * } 243 * 244 * @param <C> the type of the input elements 245 * @return a {@code Collector} implementing the min-max reduction 246 * @throws java.lang.NullPointerException if the given {@code mapper} is 247 * {@code null} 248 */ 249 public static <C extends Comparable<? super C>> 250 Collector<C, ?, MinMax<C>> toMinMax() { 251 return toMinMax(Comparator.naturalOrder()); 252 } 253 254 /** 255 * Create a new {@code MinMax} <i>consumer</i> with the given 256 * {@link java.util.Comparator}. 257 * 258 * @param comparator the comparator used for comparing two elements 259 * @param <T> the element type 260 * @return a new {@code MinMax} <i>consumer</i> 261 * @throws java.lang.NullPointerException if the {@code comparator} is 262 * {@code null}. 263 */ 264 public static <T> MinMax<T> of(final Comparator<? super T> comparator) { 265 return new MinMax<>(comparator); 266 } 267 268 /** 269 * Create a new {@code MinMax} <i>consumer</i>. 270 * 271 * @param <C> the element type 272 * @return a new {@code MinMax} <i>consumer</i> 273 */ 274 public static <C extends Comparable<? super C>> MinMax<C> of() { 275 return of(Comparator.naturalOrder()); 276 } 277 278 279 /* ************************************************************************* 280 * Some "flat" mapper functions. 281 * ************************************************************************/ 282 283 /** 284 * Return a new flat-mapper function, which guarantees a strictly increasing 285 * stream, from an arbitrarily ordered source stream. Note that this 286 * function doesn't sort the stream. It <em>just</em> skips the <em>out of 287 * order</em> elements. 288 * <p> 289 * {@snippet lang="java": 290 * final ISeq<Integer> values = new Random().ints(0, 100) 291 * .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 */ 305 public static <C extends Comparable<? super C>> 306 Function<C, Stream<C>> toStrictlyIncreasing() { 307 return Streams.toStrictlyIncreasing(); 308 } 309 310 /** 311 * Return a new flat-mapper function, which guarantees a strictly decreasing 312 * stream, from an arbitrarily ordered source stream. Note that this 313 * function doesn't sort the stream. It <em>just</em> skips the <em>out of 314 * order</em> elements. 315 * <p> 316 * {@snippet lang="java": 317 * final ISeq<Integer> values = new Random().ints(0, 100) 318 * .boxed() 319 * .limit(100) 320 * .flatMap(MinMax.toStrictlyDecreasing()) 321 * .collect(ISeq.toISeq()); 322 * 323 * System.out.println(values); 324 * // [45,32,15,12,3,1] 325 * } 326 * 327 * @since 5.0 328 * 329 * @param <C> the comparable type 330 * @return a new flat-mapper function 331 */ 332 public static <C extends Comparable<? super C>> 333 Function<C, Stream<C>> toStrictlyDecreasing() { 334 return Streams.toStrictlyDecreasing(); 335 } 336 337 /** 338 * Return a new flat-mapper function, which guarantees a strictly improving 339 * stream, from an arbitrarily ordered source stream. Note that this 340 * function doesn't sort the stream. It <em>just</em> skips the <em>out of 341 * order</em> elements. 342 * <p> 343 * {@snippet lang="java": 344 * final ISeq<Integer> values = new Random().ints(0, 100) 345 * .boxed() 346 * .limit(100) 347 * .flatMap(MinMax.toStrictlyImproving(Comparator.naturalOrder())) 348 * .collect(ISeq.toISeq()); 349 * 350 * System.out.println(values); 351 * // [6,47,65,78,96,96,99] 352 * } 353 * 354 * @since 6.0 355 * 356 * @see #toStrictlyIncreasing() 357 * @see #toStrictlyDecreasing() 358 * 359 * @param <T> the element type 360 * @param comparator the comparator used for testing the elements 361 * @return a new flat-mapper function 362 */ 363 public static <T> Function<T, Stream<T>> 364 toStrictlyImproving(final Comparator<? super T> comparator) { 365 return Streams.toStrictlyImproving(comparator); 366 } 367 368}