001/* 002 * Java Genetic Algorithm Library (jenetics-7.2.0). 003 * Copyright (c) 2007-2023 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 * <pre>{@code 041 * final Stream<Integer> stream = ... 042 * final MinMax<Integer> minMax = stream.collect( 043 * MinMax::of, 044 * MinMax::accept, 045 * MinMax::combine 046 * ); 047 * }</pre> 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 * <pre>{@code 135 * final MinMax mm1 = ...; 136 * final MinMax mm2 = ...; 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 * }</pre> 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 * <pre>{@code 214 * final Comparator<SomeObject> comparator = ... 215 * final Stream<SomeObject> stream = ... 216 * final MinMax<SomeObject> moments = stream 217 * .collect(doubleMoments.toMinMax(comparator)); 218 * }</pre> 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 * 240 * <pre>{@code 241 * final Stream<SomeObject> stream = ... 242 * final MinMax<SomeObject> moments = stream 243 * .collect(doubleMoments.toMinMax(comparator)); 244 * }</pre> 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 * 291 * <pre>{@code 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 * }</pre> 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 * 318 * <pre>{@code 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 * }</pre> 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 * 345 * <pre>{@code 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 * }</pre> 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}