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.util;
021
022import static java.time.Clock.systemUTC;
023import static java.util.Objects.requireNonNull;
024
025import java.time.Clock;
026import java.time.Duration;
027import java.util.Comparator;
028import java.util.concurrent.atomic.AtomicInteger;
029import java.util.concurrent.atomic.AtomicLong;
030import java.util.concurrent.atomic.AtomicReference;
031import java.util.function.BinaryOperator;
032import java.util.function.Function;
033import java.util.stream.Gatherer;
034import java.util.stream.Stream;
035
036/**
037 * This class contains factory methods for (flat) mapping stream element. The
038 * functions of this class can be used in the following way.
039 * {@snippet lang = "java":
040 * final ISeq<Integer> values = new Random().ints(0, 100).boxed()
041 *     .limit(100)
042 *     .gather(Streams.maxOfInterval(13))
043 *     .collect(ISeq.toISeq());
044 *}
045 *
046 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
047 * @since 6.0
048 * @version 6.0
049 */
050public final class Streams {
051        private Streams() {
052        }
053
054
055        /**
056         * Return a new flat-mapper function, which guarantees a strictly increasing
057         * stream, from an arbitrarily ordered source stream. Note that this
058         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
059         * order</em> elements.
060         *
061         * <pre>{@code
062         *     +----3--2--5--4--7--7--4--9----|
063         *        toStrictlyIncreasing()
064         *     +----3-----5-----7--------9----|
065         * }</pre>
066         *
067         * {@snippet lang="java":
068         * @SuppressWarnings("removal")
069         * final ISeq<Integer> values = new Random().ints(0, 100)
070         *     .boxed()
071         *     .limit(100)
072         *     .flatMap(Streams.toStrictlyIncreasing())
073         *     .collect(ISeq.toISeq());
074         *
075         * System.out.println(values);
076         * // [6,47,65,78,96,96,99]
077         * }
078         *
079         *
080         * @param <C> the comparable type
081         * @return a new flat-mapper function
082         *
083         * @deprecated Use the {@link #strictlyIncreasing()} method instead.
084         */
085        @Deprecated(forRemoval = true, since = "9.1")
086        public static <C extends Comparable<? super C>>
087        Function<C, Stream<C>> toStrictlyIncreasing() {
088                return strictlyImproving0(Streams::max);
089        }
090
091        /**
092         * Return a new gatherer, which guarantees a strictly increasing stream, from
093         * an arbitrarily ordered source stream. Note that this gatherer doesn't sort
094         * the stream. It <em>just</em> skips the <em>out of order</em> elements.
095         *
096         * <pre>{@code
097         *     +----3--2--5--4--7--7--4--9----|
098         *        toStrictlyIncreasing()
099         *     +----3-----5-----7--------9----|
100         * }</pre>
101         *
102         * {@snippet lang = "java":
103         * final ISeq<Integer> values = new Random().ints(0, 100)
104         *     .boxed()
105         *     .limit(100)
106         *     .gather(Streams.strictlyIncreasing())
107         *     .collect(ISeq.toISeq());
108         *
109         * System.out.println(values);
110         * // [6,47,65,78,96,96,99]
111         *}
112         *
113         * @param <C> the comparable type
114         * @return a new gatherer
115         */
116        public static <C extends Comparable<? super C>>
117        Gatherer<C, ?, C> strictlyIncreasing() {
118                return strictlyImproving(Comparator.naturalOrder());
119        }
120
121        /**
122         * Return a new flat-mapper function, which guarantees a strictly decreasing
123         * stream, from an arbitrarily ordered source stream. Note that this
124         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
125         * order</em> elements.
126         *
127         * <pre>{@code
128         *     +----9--8--9--5--6--6--2--9----|
129         *        toStrictlyDecreasing()
130         *     +----9--8-----5--------2-------|
131         * }</pre>
132         *
133         * {@snippet lang="java":
134         * @SuppressWarnings("removal")
135         * final ISeq<Integer> values = new Random().ints(0, 100)
136         *     .boxed()
137         *     .limit(100)
138         *     .flatMap(Streams.toStrictlyDecreasing())
139         *     .collect(ISeq.toISeq());
140         *
141         * System.out.println(values);
142         * // [45,32,15,12,3,1]
143         * }
144         *
145         * @param <C> the comparable type
146         * @return a new flat-mapper function
147         * @deprecated Use {@link #strictlyDecreasing()} instead.
148         */
149        @Deprecated(forRemoval = true, since = "9.1")
150        public static <C extends Comparable<? super C>>
151        Function<C, Stream<C>> toStrictlyDecreasing() {
152                return strictlyImproving0(Streams::min);
153        }
154
155        /**
156         * Return a new gatherer, which guarantees a strictly decreasin stream, from
157         * an arbitrarily ordered source stream. Note that this gatherer doesn't sort
158         * the stream. It <em>just</em> skips the <em>out of order</em> elements.
159         *
160         * <pre>{@code
161         *     +----9--8--9--5--6--6--2--9----|
162         *        strictlyDecreasing()
163         *     +----9--8-----5--------2-------|
164         * }</pre>
165         *
166         * {@snippet lang="java":
167         * final ISeq<Integer> values = new Random().ints(0, 100)
168         *     .boxed()
169         *     .limit(100)
170         *     .gather(Streams.strictlyDecreasing())
171         *     .collect(ISeq.toISeq());
172         *
173         * System.out.println(values);
174         * // [45,32,15,12,3,1]
175         * }
176         *
177         * @param <C> the comparable type
178         * @return a new flat-mapper function
179         */
180        public static <C extends Comparable<? super C>>
181        Gatherer<C, ?, C> strictlyDecreasing() {
182                return strictlyImproving(Comparator.<C>naturalOrder().reversed());
183        }
184
185        /**
186         * Return a new flat-mapper function, which guarantees a strictly improving
187         * stream, from an arbitrarily ordered source stream. Note that this
188         * function doesn't sort the stream. It <em>just</em> skips the <em>out of
189         * order</em> elements.
190         * {@snippet lang="java":
191         * @SuppressWarnings("removal")
192         * final ISeq<Integer> values = new Random().ints(0, 100)
193         *     .boxed()
194         *     .limit(100)
195         *     .flatMap(Streams.toStrictlyImproving(Comparator.naturalOrder()))
196         *     .collect(ISeq.toISeq());
197         *
198         * System.out.println(values);
199         * // [6,47,65,78,96,96,99]
200         * }
201         *
202         * @see #toStrictlyIncreasing()
203         * @see #toStrictlyDecreasing()
204         *
205         * @param <T> the element type
206         * @param comparator the comparator used for testing the elements
207         * @return a new flat-mapper function
208         * @deprecated Use {@link #strictlyImproving(Comparator)} instead.
209         */
210        @Deprecated(forRemoval = true, since = "9.1")
211        public static <T> Function<T, Stream<T>>
212        toStrictlyImproving(final Comparator<? super T> comparator) {
213                return strictlyImproving0((a, b) -> best(comparator, a, b));
214        }
215
216        private static <C> Function<C, Stream<C>>
217        strictlyImproving0(final BinaryOperator<C> comparator) {
218                requireNonNull(comparator);
219
220                return new Function<>() {
221                        private C _best;
222
223                        @Override
224                        public Stream<C> apply(final C result) {
225                                final C best = comparator.apply(_best, result);
226
227                                final Stream<C> stream = best == _best
228                                        ? Stream.empty()
229                                        : Stream.of(best);
230
231                                _best = best;
232
233                                return stream;
234                        }
235                };
236        }
237
238        /**
239         * Return a new gatherer, which guarantees a strictly improving stream, from
240         * an arbitrarily ordered source stream. Note that this gatherer doesn't sort
241         * the stream. It <em>just</em> skips the <em>out of order</em> elements.
242         * {@snippet lang = "java":
243         * final ISeq<Integer> values = new Random().ints(0, 100)
244         *     .boxed()
245         *     .limit(100)
246         *     .gather(Streams.strictlyImproving(Comparator.naturalOrder()))
247         *     .collect(ISeq.toISeq());
248         *
249         * System.out.println(values);
250         * // [6,47,65,78,96,96,99]
251         *}
252         *
253         * @param <T> the element type
254         * @param comparator the comparator used for testing the elements
255         * @return a new gatherer
256         * @throws NullPointerException if the given {@code comparator} is
257         *         {@code null}
258         */
259        public static <T> Gatherer<T, ?, T>
260        strictlyImproving(final Comparator<? super T> comparator) {
261                requireNonNull(comparator);
262
263                return Gatherer.ofSequential(
264                        AtomicReference<T>::new,
265                        (state, value, downstream) -> {
266                                final T best = best(comparator, state.get(), value);
267                                if (best != state.get()) {
268                                        state.set(best);
269                                        return downstream.push(best);
270                                }
271                                return true;
272                        }
273                );
274        }
275
276        private static <T extends Comparable<? super T>> T max(final T a, final T b) {
277                return best(Comparator.naturalOrder(), a, b);
278        }
279
280        private static <T extends Comparable<? super T>> T min(final T a, final T b) {
281                return best(Comparator.reverseOrder(), a, b);
282        }
283
284        private static <T>
285        T best(final Comparator<? super T> comparator, final T a, final T b) {
286                if (a == null && b == null) return null;
287                if (a == null) return b;
288                if (b == null) return a;
289                return comparator.compare(a, b) >= 0 ? a : b;
290        }
291
292        /**
293         * Return a new flat-mapper function which returns (emits) the maximal value
294         * of the last <em>n</em> elements.
295         *
296         * <pre>{@code
297         *          +----3---+----3---+
298         *          |        |        |
299         *     +----9--8--3--3--5--4--2--9----|
300         *        toIntervalMax(3)
301         *     +----------9--------5----------|
302         * }</pre>
303         *
304         * @param size the size of the slice
305         * @param <C> the element type
306         * @return a new flat-mapper function
307         * @throws IllegalArgumentException if the given size is smaller than one
308         * @deprecated Will be removed. Use {@link #maxOfInterval(int)}
309         *             instead.
310         */
311        @Deprecated(forRemoval = true, since = "9.1")
312        public static <C extends Comparable<? super C>>
313        Function<C, Stream<C>> toIntervalMax(final int size) {
314                return sliceBest1(Streams::max, size);
315        }
316
317        /**
318         * Return a new flat-mapper function which returns (emits) the minimal value
319         * of the last <em>n</em> elements.
320         *
321         * <pre>{@code
322         *          +----3---+----3---+
323         *          |        |        |
324         *     +----9--8--3--3--1--4--2--9----|
325         *        toIntervalMin(3)
326         *     +----------3--------1----------|
327         * }</pre>
328         *
329         * @param size the size of the slice
330         * @param <C> the element type
331         * @return a new flat-mapper function
332         * @throws IllegalArgumentException if the given size is smaller than one
333         * @deprecated Will be removed. Use {@link #minOfInterval(int)}
334         *             instead.
335         */
336        @Deprecated(forRemoval = true, since = "9.1")
337        public static <C extends Comparable<? super C>>
338        Function<C, Stream<C>> toIntervalMin(final int size) {
339                return sliceBest1(Streams::min, size);
340        }
341
342        /**
343         * Return a new flat-mapper function which returns (emits) the minimal value
344         * of the last <em>n</em> elements.
345         *
346         * @see #toIntervalMax(int)
347         * @see #toIntervalMin(int)
348         *
349         * @param <C> the element type
350         * @param size the size of the slice
351         * @param comparator the comparator used for testing the elements
352         * @return a new flat-mapper function
353         * @throws IllegalArgumentException if the given size is smaller than one
354         * @throws NullPointerException if the given {@code comparator} is
355         *         {@code null}
356         * @deprecated Will be removed. Use {@link #bestOfInterval(Comparator, int)}
357         *             instead.
358         */
359        @Deprecated(forRemoval = true, since = "9.1")
360        public static <C> Function<C, Stream<C>>
361        toIntervalBest(final Comparator<? super C> comparator, final int size) {
362                requireNonNull(comparator);
363                return sliceBest1((a, b) -> best(comparator, a, b), size);
364        }
365
366        @Deprecated(forRemoval = true, since = "9.1")
367        private static <C> Function<C, Stream<C>> sliceBest1(
368                final BinaryOperator<C> comp,
369                final int rangeSize
370        ) {
371                requireNonNull(comp);
372                if (rangeSize < 1) {
373                        throw new IllegalArgumentException(
374                                "Range size must be at least one: " + rangeSize
375                        );
376                }
377
378                return new Function<>() {
379                        private int _count = 0;
380                        private C _best;
381
382                        @Override
383                        public Stream<C> apply(final C value) {
384                                ++_count;
385                                _best = comp.apply(_best, value);
386
387                                final Stream<C> result;
388                                if (_count >= rangeSize) {
389                                        result = Stream.of(_best);
390                                        _count = 0;
391                                        _best = null;
392                                } else {
393                                        result = Stream.empty();
394                                }
395
396                                return result;
397                        }
398                };
399        }
400
401        /**
402         * Return a new gatherer which emits the maximum element of the last <em>n</em>
403         * consumed elements. If the stream ends with a final interval smaller than
404         * {@code n}, the maximum element of this remaining interval is emitted as
405         * well.
406         *
407         * <pre>{@code
408         *          +----3---+----3---+
409         *          |        |        |--+
410         *     +----9--8--3--3--5--4--2--9----|
411         *        maxOfInterval(3)
412         *     +----------9--------5-----9----|
413         * }</pre>
414         *
415         * @param size the size of the slice
416         * @param <C> the element type
417         * @return a new gatherer
418         * @throws IllegalArgumentException if the given size is smaller than one
419         */
420        public static <C extends Comparable<? super C>>
421        Gatherer<C, ?, C> maxOfInterval(final int size) {
422                return sliceBestGatherer(Streams::max, size);
423        }
424
425        /**
426         * Return a new gatherer which emits the minimum element of the last <em>n</em>
427         * consumed elements. If the stream ends with a final interval smaller than
428         * {@code n}, the minimum element of this remaining interval is emitted as
429         * well.
430         *
431         * <pre>{@code
432         *          +----3---+----3---+
433         *          |        |        |--+
434         *     +----9--8--3--3--1--4--2--9----|
435         *        minOfInterval(3)
436         *     +----------3--------1-----2----|
437         * }</pre>
438         *
439         * @param size the size of the slice
440         * @param <C> the element type
441         * @return a new gatherer
442         * @throws IllegalArgumentException if the given size is smaller than one
443         */
444        public static <C extends Comparable<? super C>>
445        Gatherer<C, ?, C> minOfInterval(final int size) {
446                return sliceBestGatherer(Streams::min, size);
447        }
448
449        /**
450         * Return a new gatherer which emits the best element of the last <em>n</em>
451         * consumed elements. If the stream ends with a final interval smaller than
452         * {@code n}, the best element of this remaining interval is emitted as well.
453         *
454         * @see #maxOfInterval(int)
455         * @see #minOfInterval(int)
456         *
457         * @param <C> the element type
458         * @param size the size of the slice
459         * @param comparator the comparator used for testing the elements
460         * @return a new gatherer
461         * @throws IllegalArgumentException if the given size is smaller than one
462         * @throws NullPointerException if the given {@code comparator} is
463         *         {@code null}
464         */
465        public static <C> Gatherer<C, ?, C>
466        bestOfInterval(final Comparator<? super C> comparator, final int size) {
467                requireNonNull(comparator);
468                return sliceBestGatherer((a, b) -> best(comparator, a, b), size);
469        }
470
471        private static <C> Gatherer<C, ?, C> sliceBestGatherer(
472                final BinaryOperator<C> comp,
473                final int rangeSize
474        ) {
475                requireNonNull(comp);
476                if (rangeSize < 1) {
477                        throw new IllegalArgumentException(
478                                "Range size must be at least one: " + rangeSize
479                        );
480                }
481
482                record State<C>(AtomicInteger count, AtomicReference<C> best) {
483                        State() {
484                                this(new AtomicInteger(), new AtomicReference<>());
485                        }
486                }
487
488                return Gatherer.ofSequential(
489                        State<C>::new,
490                        (state, value, downstream) -> {
491                                final var count = state.count.incrementAndGet();
492                                state.best.set(comp.apply(state.best.get(), value));
493
494                                if (count >= rangeSize) {
495                                        downstream.push(state.best.get());
496                                        state.count.set(0);
497                                        state.best.set(null);
498                                }
499
500                                return true;
501                        },
502                        (state, downstream) -> {
503                                if (state.count.get() > 0) {
504                                        downstream.push(state.best.get());
505                                }
506                        }
507                );
508
509        }
510
511        /**
512         * Return a new flat-mapper function which returns (emits) the maximal value
513         * of the elements emitted within the given {@code timespan}.
514         *
515         * <pre>{@code
516         *          +---3s---+---3s---+
517         *          |        |        |
518         *     +----9--8--3--3--5--4--2--9----|
519         *        toIntervalMax(3s)
520         *     +----------9--------5----------|
521         * }</pre>
522         *
523         * @see #toIntervalMax(Duration, Clock)
524         *
525         * @param <C> the element type
526         * @param timespan the timespan the elements are collected for the
527         *        calculation slice
528         * @return a new flat-mapper function
529         * @throws IllegalArgumentException if the given size is smaller than one
530         * @throws NullPointerException if the given {@code timespan} is {@code null}
531         * @deprecated Will be removed. Use {@link #maxOfInterval(Duration)}
532         *             instead.
533         */
534        @Deprecated(forRemoval = true, since = "9.1")
535        public static <C extends Comparable<? super C>>
536        Function<C, Stream<C>> toIntervalMax(final Duration timespan) {
537                return sliceBest0(Streams::max, timespan, systemUTC());
538        }
539
540        /**
541         * Return a new flat-mapper function which returns (emits) the maximal value
542         * of the elements emitted within the given {@code timespan}.
543         *
544         * <pre>{@code
545         *          +---3s---+---3s---+
546         *          |        |        |
547         *     +----9--8--3--3--5--4--2--9----|
548         *        toIntervalMax(3s)
549         *     +----------9--------5----------|
550         * }</pre>
551         *
552         * @see #toIntervalMax(Duration)
553         *
554         * @param <C> the element type
555         * @param timespan the timespan the elements are collected for the
556         *        calculation slice
557         * @param clock the {@code clock} used for measuring the {@code timespan}
558         * @return a new flat-mapper function
559         * @throws IllegalArgumentException if the given size is smaller than one
560         * @throws NullPointerException if one of the arguments is {@code null}
561         * @deprecated Will be removed. Use {@link #maxOfInterval(Duration, Clock)}
562         *             instead.
563         */
564        @Deprecated(forRemoval = true, since = "9.1")
565        public static <C extends Comparable<? super C>>
566        Function<C, Stream<C>> toIntervalMax(final Duration timespan, final Clock clock) {
567                return sliceBest0(Streams::max, timespan, clock);
568        }
569
570        /**
571         * Return a new flat-mapper function which returns (emits) the minimal value
572         * of the elements emitted within the given {@code timespan}.
573         *
574         * <pre>{@code
575         *          +---3s---+---3s---+
576         *          |        |        |
577         *     +----9--8--3--3--1--4--2--9----|
578         *        toIntervalMin(3s)
579         *     +----------3--------1----------|
580         * }</pre>
581         *
582         * @see #toIntervalMin(Duration, Clock)
583         *
584         * @param <C> the element type
585         * @param timespan the timespan the elements are collected for the
586         *        calculation slice
587         * @return a new flat-mapper function
588         * @throws IllegalArgumentException if the given size is smaller than one
589         * @throws NullPointerException if the given {@code timespan} is {@code null}
590         * @deprecated Will be removed. Use {@link #minOfInterval(Duration)}
591         *             instead.
592         */
593        @Deprecated(forRemoval = true, since = "9.1")
594        public static <C extends Comparable<? super C>>
595        Function<C, Stream<C>> toIntervalMin(final Duration timespan) {
596                return sliceBest0(Streams::min, timespan, systemUTC());
597        }
598
599        /**
600         * Return a new flat-mapper function which returns (emits) the minimal value
601         * of the elements emitted within the given {@code timespan}.
602         *
603         * <pre>{@code
604         *          +---3s---+---3s---+
605         *          |        |        |
606         *     +----9--8--3--3--1--4--2--9----|
607         *        toIntervalMin(3s)
608         *     +----------3--------1----------|
609         * }</pre>
610         *
611         * @see #toIntervalMin(Duration)
612         *
613         * @param <C> the element type
614         * @param timespan the timespan the elements are collected for the
615         *        calculation slice
616         * @param clock the {@code clock} used for measuring the {@code timespan}
617         * @return a new flat-mapper function
618         * @throws IllegalArgumentException if the given size is smaller than one
619         * @throws NullPointerException if one of the arguments is {@code null}
620         * @deprecated Will be removed. Use {@link #minOfInterval(Duration, Clock)}
621         *             instead.
622         */
623        @Deprecated(forRemoval = true, since = "9.1")
624        public static <C extends Comparable<? super C>>
625        Function<C, Stream<C>> toIntervalMin(final Duration timespan, final Clock clock) {
626                return sliceBest0(Streams::min, timespan, clock);
627        }
628
629        /**
630         * Return a new flat-mapper function which returns (emits) the minimal value
631         * of the elements emitted within the given {@code timespan}.
632         *
633         * @see #toIntervalMin(Duration)
634         * @see #toIntervalMax(Duration)
635         *
636         * @param <C> the element type
637         * @param comparator the comparator used for testing the elements
638         * @param timespan the timespan the elements are collected for the
639         *        calculation slice
640         * @return a new flat-mapper function
641         * @throws IllegalArgumentException if the given size is smaller than one
642         * @throws NullPointerException if one of the arguments is {@code null}
643         * @deprecated Will be removed. Use {@link #bestOfInterval(Comparator, Duration)}
644         *             instead.
645         */
646        @Deprecated(forRemoval = true, since = "9.1")
647        public static <C> Function<C, Stream<C>>
648        toIntervalBest(final Comparator<? super C> comparator, final Duration timespan) {
649                requireNonNull(comparator);
650                return sliceBest0((a, b) -> best(comparator, a, b), timespan, systemUTC());
651        }
652
653        /**
654         * Return a new flat-mapper function which returns (emits) the minimal value
655         * of the elements emitted within the given {@code timespan}.
656         *
657         * @param <C> the element type
658         * @param comparator the comparator used for testing the elements
659         * @param timespan the timespan the elements are collected for the
660         *        calculation slice
661         * @param clock the {@code clock} used for measuring the {@code timespan}
662         * @return a new flat-mapper function
663         * @throws IllegalArgumentException if the given size is smaller than one
664         * @throws NullPointerException if one of the arguments is {@code null}
665         * @deprecated Will be removed. Use {@link #bestOfInterval(Comparator, Duration, Clock)}
666         *             instead.
667         */
668        @Deprecated(forRemoval = true, since = "9.1")
669        public static <C> Function<C, Stream<C>>
670        toIntervalBest(
671                final Comparator<? super C> comparator,
672                final Duration timespan,
673                final Clock clock
674        ) {
675                requireNonNull(comparator);
676                return sliceBest0((a, b) -> best(comparator, a, b), timespan, clock);
677        }
678
679        @Deprecated(forRemoval = true, since = "9.1")
680        private static <C> Function<C, Stream<C>> sliceBest0(
681                final BinaryOperator<C> comp,
682                final Duration timespan,
683                final Clock clock
684        ) {
685                requireNonNull(comp);
686                requireNonNull(timespan);
687                requireNonNull(clock);
688
689                return new Function<>() {
690                        private final long _timespan  = timespan.toMillis();
691
692                        private long _start = 0;
693                        private C _best;
694
695                        @Override
696                        public Stream<C> apply(final C value) {
697                                if (_start == 0) {
698                                        _start = clock.millis();
699                                }
700
701                                _best = comp.apply(_best, value);
702                                long end = clock.millis();
703
704                                final Stream<C> result;
705                                if (end - _start >= _timespan) {
706                                        result = Stream.of(_best);
707                                        _start = 0;
708                                        _best = null;
709                                } else {
710                                        result = Stream.empty();
711                                }
712
713                                return result;
714                        }
715                };
716        }
717
718        /**
719         * Return a new gatherer which emits the maximal element consumed within the
720         * given {@code timespan}.
721         *
722         * <pre>{@code
723         *          +---3s---+---3s---+
724         *          |        |        |
725         *     +----9--8--3--3--5--4--2--9----|
726         *        maxOfInterval(3s)
727         *     +----------9--------5----------|
728         * }</pre>
729         *
730         * @see #maxOfInterval(Duration, Clock)
731         *
732         * @param <C> the element type
733         * @param timespan the timespan the elements are collected for the
734         *        calculation slice
735         * @return a new gatherer
736         * @throws IllegalArgumentException if the given size is smaller than one
737         * @throws NullPointerException if the given {@code timespan} is {@code null}
738         */
739        public static <C extends Comparable<? super C>>
740        Gatherer<C, ?, C> maxOfInterval(final Duration timespan) {
741                return sliceBestGatherer(Streams::max, timespan, systemUTC());
742        }
743
744        /**
745         * Return a new gatherer which emits the maximal element consumed within the
746         * given {@code timespan}.
747         *
748         * <pre>{@code
749         *          +---3s---+---3s---+
750         *          |        |        |
751         *     +----9--8--3--3--5--4--2--9----|
752         *        maxOfInterval(3s)
753         *     +----------9--------5----------|
754         * }</pre>
755         *
756         * @see #maxOfInterval(Duration)
757         *
758         * @param <C> the element type
759         * @param timespan the timespan the elements are collected for the
760         *        calculation slice
761         * @param clock the {@code clock} used for measuring the {@code timespan}
762         * @return a new gatherer
763         * @throws IllegalArgumentException if the given size is smaller than one
764         * @throws NullPointerException if one of the arguments is {@code null}
765         */
766        public static <C extends Comparable<? super C>>
767        Gatherer<C, ?, C> maxOfInterval(final Duration timespan, final Clock clock) {
768                return sliceBestGatherer(Streams::max, timespan, clock);
769        }
770
771        /**
772         * Return a new gatherer which emits the minimal element consumed within the
773         * given {@code timespan}.
774         *
775         * <pre>{@code
776         *          +---3s---+---3s---+
777         *          |        |        |
778         *     +----9--8--3--3--1--4--2--9----|
779         *        minOfInterval(3s)
780         *     +----------3--------1----------|
781         * }</pre>
782         *
783         * @see #minOfInterval(Duration, Clock)
784         *
785         * @param <C> the element type
786         * @param timespan the timespan the elements are collected for the
787         *        calculation slice
788         * @return a new gatherer
789         * @throws IllegalArgumentException if the given size is smaller than one
790         * @throws NullPointerException if the given {@code timespan} is {@code null}
791         */
792        public static <C extends Comparable<? super C>>
793        Gatherer<C, ?, C> minOfInterval(final Duration timespan) {
794                return sliceBestGatherer(Streams::min, timespan, systemUTC());
795        }
796
797        /**
798         * Return a new gatherer which emits the minimal element consumed within the
799         * given {@code timespan}.
800         *
801         * <pre>{@code
802         *          +---3s---+---3s---+
803         *          |        |        |
804         *     +----9--8--3--3--1--4--2--9----|
805         *        minOfInterval(3s)
806         *     +----------3--------1----------|
807         * }</pre>
808         *
809         * @see #minOfInterval(Duration)
810         *
811         * @param <C> the element type
812         * @param timespan the timespan the elements are collected for the
813         *        calculation slice
814         * @param clock the {@code clock} used for measuring the {@code timespan}
815         * @return a new gatherer
816         * @throws IllegalArgumentException if the given size is smaller than one
817         * @throws NullPointerException if one of the arguments is {@code null}
818         */
819        public static <C extends Comparable<? super C>>
820        Gatherer<C, ?, C> minOfInterval(final Duration timespan, final Clock clock) {
821                return sliceBestGatherer(Streams::min, timespan, clock);
822        }
823
824        /**
825         * Return a new gatherer which emits the <em>best</em> element consumed
826         * within the given {@code timespan}.
827         *
828         * @see #minOfInterval(Duration)
829         * @see #maxOfInterval(Duration)
830         *
831         * @param <C> the element type
832         * @param comparator the comparator used for testing the elements
833         * @param timespan the timespan the elements are collected for the
834         *        calculation slice
835         * @return a new gatherer
836         * @throws IllegalArgumentException if the given size is smaller than one
837         * @throws NullPointerException if one of the arguments is {@code null}
838         */
839        public static <C>Gatherer<C, ?, C>
840        bestOfInterval(final Comparator<? super C> comparator, final Duration timespan) {
841                requireNonNull(comparator);
842                return sliceBestGatherer((a, b) -> best(comparator, a, b), timespan, systemUTC());
843        }
844
845        /**
846         * Return a new gatherer which emits the <em>best</em> element consumed
847         * within the given {@code timespan}.
848         *
849         * @param <C> the element type
850         * @param comparator the comparator used for testing the elements
851         * @param timespan the timespan the elements are collected for the
852         *        calculation slice
853         * @param clock the {@code clock} used for measuring the {@code timespan}
854         * @return a new gatherer
855         * @throws IllegalArgumentException if the given size is smaller than one
856         * @throws NullPointerException if one of the arguments is {@code null}
857         */
858        public static <C> Gatherer<C, ?, C> bestOfInterval(
859                final Comparator<? super C> comparator,
860                final Duration timespan,
861                final Clock clock
862        ) {
863                requireNonNull(comparator);
864                return sliceBestGatherer((a, b) -> best(comparator, a, b), timespan, clock);
865        }
866
867
868        private static <C> Gatherer<C, ?, C> sliceBestGatherer(
869                final BinaryOperator<C> comp,
870                final Duration timespan,
871                final Clock clock
872        ) {
873                requireNonNull(comp);
874                requireNonNull(timespan);
875                requireNonNull(clock);
876
877                record State<C>(long ts, AtomicLong start, AtomicReference<C> best) {
878                        State(long ts) {
879                                this(ts, new AtomicLong(0), new AtomicReference<>());
880                        }
881                }
882
883                return Gatherer.ofSequential(
884                        () -> new State<C>(timespan.toMillis()),
885                        (state, value, downstream) -> {
886                                if (state.start.get() == 0) {
887                                        state.start.set(clock.millis());
888                                }
889
890                                state.best.set(comp.apply(state.best.get(), value));
891                                long end = clock.millis();
892
893                                if (end - state.start.get() >= state.ts) {
894                                        downstream.push(state.best.get());
895                                        state.start.set(0);
896                                        state.best.set(null);
897                                }
898
899                                return true;
900                        }
901                );
902        }
903
904}