content: package com.annimon.stream;
  
  import com.annimon.stream.function.BinaryOperator;
  import com.annimon.stream.function.Consumer;
  import com.annimon.stream.function.DoubleBinaryOperator;
  import com.annimon.stream.function.Function;
  import com.annimon.stream.function.IntBinaryOperator;
  import com.annimon.stream.function.LongBinaryOperator;
  import com.annimon.stream.iterator.LsaIterator;
  import com.annimon.stream.iterator.PrimitiveIterator;
  import java.util.ArrayDeque;
  import java.util.Iterator;
  
  /**
   * Custom operator examples for {@code Stream.custom} method.
   *
   * @see com.annimon.stream.Stream#custom(com.annimon.stream.function.Function)
   */
  public final class CustomOperators {
  
      private CustomOperators() { }
  
      /**
       * Example of intermediate operator, that produces reversed stream.
       *
       * @param <T>
       */
      public static class Reverse<T> implements Function<Stream<T>, Stream<T>> {
  
          @Override
          public Stream<T> apply(Stream<T> stream) {
              final Iterator<? extends T> iterator = stream.iterator();
              final ArrayDeque<T> deque = new ArrayDeque<T>();
              while (iterator.hasNext()) {
                  deque.addFirst(iterator.next());
              }
              return Stream.of(deque.iterator());
          }
      }
  
      /**
       * Example of combining {@code Stream} operators.
       *
       * @param <T>
       */
      public static class SkipAndLimit<T> implements Function<Stream<T>, Stream<T>> {
  
          private final int skip, limit;
  
          public SkipAndLimit(int skip, int limit) {
              this.skip = skip;
              this.limit = limit;
          }
  
          @Override
          public Stream<T> apply(Stream<T> stream) {
              return stream.skip(skip).limit(limit);
          }
      }
  
      /**
       * Example of terminal operator, that reduces stream to calculate sum on integer elements.
       */
      public static class Sum implements Function<Stream<Integer>, Integer> {
  
          @Override
          public Integer apply(Stream<Integer> stream) {
              return stream.reduce(0, new BinaryOperator<Integer>() {
                  @Override
                  public Integer apply(Integer value1, Integer value2) {
                      return value1 + value2;
                  }
              });
          }
      }
  
      /**
       * Example of terminal forEach operator.
       *
       * @param <T>
       */
      public static class ForEach<T> implements Function<Stream<T>, Void> {
  
          private final Consumer<? super T> action;
  
          public ForEach(Consumer<? super T> action) {
              this.action = action;
          }
  
          @Override
          public Void apply(Stream<T> stream) {
              final Iterator<? extends T> iterator = stream.iterator();
              while (iterator.hasNext()) {
                  action.accept(iterator.next());
              }
              return null;
          }
      }
  
      /**
       * Example of intermediate operator, that casts elements in the stream.
       *
       * @param <T>
       * @param <R>
       */
      public static class Cast<T, R> implements Function<Stream<T>, Stream<R>> {
  
          private final Class<R> clazz;
  
          public Cast(Class<R> clazz) {
              this.clazz = clazz;
          }
  
          @Override
          public Stream<R> apply(final Stream<T> stream) {
              final Iterator<? extends T> iterator = stream.iterator();
              return Stream.of(new LsaIterator<R>() {
  
                  @Override
                  public boolean hasNext() {
                      return iterator.hasNext();
                  }
  
                  @Override
                  public R nextIteration() {
                      return clazz.cast(iterator.next());
                  }
              });
          }
      }
  
      /**
       * Example of intermediate flatMap operator.
       *
       * @param <T>
       * @param <R>
       */
      public static class FlatMap<T, R> implements Function<Stream<T>, Stream<R>> {
  
          private final Function<? super T, ? extends Stream<R>> mapper;
  
          public FlatMap(Function<? super T, ? extends Stream<R>> mapper) {
              this.mapper = mapper;
          }
  
          @Override
          public Stream<R> apply(final Stream<T> stream) {
              final Iterator<? extends T> iterator = stream.iterator();
              return Stream.of(new LsaIterator<R>() {
  
                  private Iterator<? extends R> inner;
  
                  @Override
                  public boolean hasNext() {
                      fillInnerIterator();
                      return inner != null && inner.hasNext();
                  }
  
                  @Override
                  public R nextIteration() {
                      fillInnerIterator();
                      return inner.next();
                  }
  
                  private void fillInnerIterator() {
                      if ((inner != null) && inner.hasNext()) {
                          return;
                      }
                      while (iterator.hasNext()) {
                          final T arg = iterator.next();
                          final Stream<? extends R> result = mapper.apply(arg);
                          if (result != null) {
                              inner = result.iterator();
                              if (inner.hasNext()) {
                                  return;
                              }
                          }
                      }
                  }
  
              });
          }
      }
  
      /**
       * Example of intermediate zip operator applying on two {@code IntStream}.
       */
      public static class Zip implements Function<IntStream, IntStream> {
  
          private final IntStream secondStream;
          private final IntBinaryOperator combiner;
  
          public Zip(IntStream secondStream, IntBinaryOperator combiner) {
              this.secondStream = secondStream;
              this.combiner = combiner;
          }
  
          @Override
          public IntStream apply(IntStream firstStream) {
              final PrimitiveIterator.OfInt it1 = firstStream.iterator();
              final PrimitiveIterator.OfInt it2 = secondStream.iterator();
              return IntStream.of(new PrimitiveIterator.OfInt() {
  
                  @Override
                  public boolean hasNext() {
                      return it1.hasNext() && it2.hasNext();
                  }
  
                  @Override
                  public int nextInt() {
                      return combiner.applyAsInt(it1.nextInt(), it2.nextInt());
                  }
              });
          }
      }
  
      /**
       * Example of intermediate zip operator applying on two {@code LongStream}.
       */
      public static class ZipLong implements Function<LongStream, LongStream> {
  
          private final LongStream secondStream;
          private final LongBinaryOperator combiner;
  
          public ZipLong(LongStream secondStream, LongBinaryOperator combiner) {
              this.secondStream = secondStream;
              this.combiner = combiner;
          }
  
          @Override
          public LongStream apply(LongStream firstStream) {
              final PrimitiveIterator.OfLong it1 = firstStream.iterator();
              final PrimitiveIterator.OfLong it2 = secondStream.iterator();
              return LongStream.of(new PrimitiveIterator.OfLong() {
  
                  @Override
                  public boolean hasNext() {
                      return it1.hasNext() && it2.hasNext();
                  }
  
                  @Override
                  public long nextLong() {
                      return combiner.applyAsLong(it1.nextLong(), it2.nextLong());
                  }
              });
          }
      }
  
      /**
       * Example of terminal operator that calculates arithmetic mean of values.
       */
      public static class Average implements Function<IntStream, Double> {
  
          @Override
          public Double apply(IntStream stream) {
              long count = 0, sum = 0;
              final PrimitiveIterator.OfInt it = stream.iterator();
              while (it.hasNext()) {
                  count++;
                  sum += it.nextInt();
              }
              return (count == 0) ? 0 : sum / (double) count;
          }
      }
  
      /**
       * Example of terminal operator that calculates arithmetic mean of values.
       */
      public static class AverageLong implements Function<LongStream, Double> {
  
          @Override
          public Double apply(LongStream stream) {
              long count = 0, sum = 0;
              final PrimitiveIterator.OfLong it = stream.iterator();
              while (it.hasNext()) {
                  count++;
                  sum += it.nextLong();
              }
              return (count == 0) ? 0 : sum / (double) count;
          }
      }
  
      /**
       * Example of intermediate zip operator applying on {@code DoubleStream} and {@code IntStream}.
       */
      public static class ZipWithIntStream implements Function<DoubleStream, DoubleStream> {
  
          private final IntStream secondStream;
          private final DoubleBinaryOperator combiner;
  
          public ZipWithIntStream(IntStream secondStream, DoubleBinaryOperator combiner) {
              this.secondStream = secondStream;
              this.combiner = combiner;
          }
  
          @Override
          public DoubleStream apply(DoubleStream firstStream) {
              final PrimitiveIterator.OfDouble it1 = firstStream.iterator();
              final PrimitiveIterator.OfInt it2 = secondStream.iterator();
              return DoubleStream.of(new PrimitiveIterator.OfDouble() {
  
                  @Override
                  public boolean hasNext() {
                      return it1.hasNext() && it2.hasNext();
                  }
  
                  @Override
                  public double nextDouble() {
                      return combiner.applyAsDouble(it1.nextDouble(), it2.nextInt());
                  }
              });
          }
      }
  
      /**
       * Example of terminal operator that calculates count, sum, and arithmetic mean of values.
       */
      public static class DoubleSummaryStatistics implements Function<DoubleStream, double[]> {
  
          @Override
          public double[] apply(DoubleStream stream) {
              long count = 0;
              double sum = 0;
              final PrimitiveIterator.OfDouble it = stream.iterator();
              while (it.hasNext()) {
                  count++;
                  sum += it.nextDouble();
              }
              double average = (count == 0) ? 0 : (sum / (double) count);
              return new double[] {count, sum, average};
          }
      }
  }
  