001/* 002 * Java Genetic Algorithm Library (jenetics-8.1.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 */ 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 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 * {@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) 292 * .boxed() 293 * .limit(100) 294 * .flatMap(MinMax.toStrictlyIncreasing()) 295 * .collect(ISeq.toISeq()); 296 * 297 * System.out.println(values); 298 * // [6,47,65,78,96,96,99] 299 * } 300 * 301 * @since 5.0 302 * 303 * @param <C> the comparable type 304 * @return a new flat-mapper function 305 */ 306 public static <C extends Comparable<? super C>> 307 Function<C, Stream<C>> toStrictlyIncreasing() { 308 return Streams.toStrictlyIncreasing(); 309 } 310 311 /** 312 * Return a new flat-mapper function, which guarantees a strictly decreasing 313 * stream, from an arbitrarily ordered source stream. Note that this 314 * function doesn't sort the stream. It <em>just</em> skips the <em>out of 315 * order</em> elements. 316 * <p> 317 * {@snippet lang="java": 318 * final ISeq<Integer> values = new Random().ints(0, 100) 319 * .boxed() 320 * .limit(100) 321 * .flatMap(MinMax.toStrictlyDecreasing()) 322 * .collect(ISeq.toISeq()); 323 * 324 * System.out.println(values); 325 * // [45,32,15,12,3,1] 326 * } 327 * 328 * @since 5.0 329 * 330 * @param <C> the comparable type 331 * @return a new flat-mapper function 332 */ 333 public static <C extends Comparable<? super C>> 334 Function<C, Stream<C>> toStrictlyDecreasing() { 335 return Streams.toStrictlyDecreasing(); 336 } 337 338 /** 339 * Return a new flat-mapper function, which guarantees a strictly improving 340 * stream, from an arbitrarily ordered source stream. Note that this 341 * function doesn't sort the stream. It <em>just</em> skips the <em>out of 342 * order</em> elements. 343 * <p> 344 * {@snippet lang="java": 345 * final ISeq<Integer> values = new Random().ints(0, 100) 346 * .boxed() 347 * .limit(100) 348 * .flatMap(MinMax.toStrictlyImproving(Comparator.naturalOrder())) 349 * .collect(ISeq.toISeq()); 350 * 351 * System.out.println(values); 352 * // [6,47,65,78,96,96,99] 353 * } 354 * 355 * @since 6.0 356 * 357 * @see #toStrictlyIncreasing() 358 * @see #toStrictlyDecreasing() 359 * 360 * @param <T> the element type 361 * @param comparator the comparator used for testing the elements 362 * @return a new flat-mapper function 363 */ 364 public static <T> Function<T, Stream<T>> 365 toStrictlyImproving(final Comparator<? super T> comparator) { 366 return Streams.toStrictlyImproving(comparator); 367 } 368 369}