All Downloads are FREE. Search and download functionalities are using the official Maven repository.

functionalj.stream.IntStreamPlus Maven / Gradle / Ivy

There is a newer version: 1.0.17
Show newest version
// ============================================================================
// Copyright (c) 2017-2019 Nawapunth Manusitthipol (NawaMan - http://nawaman.net).
// ----------------------------------------------------------------------------
// MIT License
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// ============================================================================
package functionalj.stream;

import java.util.ArrayList;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.PrimitiveIterator;
import java.util.Set;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.IntBinaryOperator;
import java.util.function.IntConsumer;
import java.util.function.IntFunction;
import java.util.function.IntPredicate;
import java.util.function.IntSupplier;
import java.util.function.IntToDoubleFunction;
import java.util.function.IntToLongFunction;
import java.util.function.IntUnaryOperator;
import java.util.function.ObjIntConsumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import functionalj.function.Func1;
import functionalj.function.Func2;
import functionalj.function.Func3;
import functionalj.function.Func4;
import functionalj.function.Func5;
import functionalj.function.Func6;
import functionalj.function.FuncUnit1;
import functionalj.functions.StrFuncs;
import functionalj.list.FuncList;
import functionalj.list.ImmutableList;
import functionalj.pipeable.Pipeable;
import functionalj.tuple.Tuple2;
import functionalj.tuple.Tuple3;
import functionalj.tuple.Tuple4;
import functionalj.tuple.Tuple5;
import functionalj.tuple.Tuple6;
import lombok.val;

@FunctionalInterface
public interface IntStreamPlus extends IntStream {
    
    public static IntStreamPlus of(int ... ints) {
        return IntStreamPlus.from(IntStream.of(ints));
    }
    
    public static IntStreamPlus from(IntStream intStream) {
        return ()->intStream;
    }
    
    public static IntStreamPlus concat(IntStreamPlus ... streams) {
        return StreamPlus.of(streams).flatMap(s -> s.asStream()).mapToInt(i -> i);
    }
    
    public static IntStreamPlus empty() {
        return IntStreamPlus.from(IntStream.empty());
    }
    
    public static IntStreamPlus infinite() {
        return IntStreamPlus.from(IntStream.iterate(0, i -> i + 1));
    }
    
    public static IntStreamPlus range(int startInclusive, int endExclusive) {
        return IntStreamPlus.from(IntStream.range(startInclusive, endExclusive));
    }
    
    public static IntStreamPlus rangeClosed(int startInclusive, int endInclusive) {
        return IntStreamPlus.from(IntStream.rangeClosed(startInclusive, endInclusive));
    }
    
    public static IntStreamPlus generate(IntSupplier s) {
        return IntStreamPlus.from(IntStream.generate(s));
    }
    
    public static IntStreamPlus iterate(int seed, IntUnaryOperator f) {
        return IntStreamPlus.from(IntStream.iterate(seed, f));
    }
    
    public static IntStreamPlus compound(int seed, IntUnaryOperator f) {
        return IntStreamPlus.from(IntStream.iterate(seed, f));
    }
    
    public static IntStreamPlus iterate(int seed1, int seed2, IntBinaryOperator f) {
        val counter = new AtomicInteger(0);
        val int1    = new AtomicInteger(seed1);
        val int2    = new AtomicInteger(seed2);
        return IntStreamPlus.generate(()->{
            if (counter.getAndIncrement() == 0)
                return seed1;
            if (counter.getAndIncrement() == 2)
                return seed2;
            
            int i2 = int2.get();
            int i1 = int1.getAndSet(i2);
            int i  = f.applyAsInt(i1, i2);
            int2.set(i);
            return i;
        });
    }
    
    //== Stream ==
    
    public IntStream stream();
    
    public default  TARGET terminate(Func1 action) {
        val stream = stream();
        try {
            val result = action.apply(stream);
            return result;
        } finally {
            stream.close();
        }
    }
    
    public default void terminate(FuncUnit1 action) {
        val stream = stream();
        try {
            action.accept(stream);
        } finally {
            stream.close();
        }
    }
    
    @Override
    public default IntStreamPlus filter(IntPredicate predicate) {
        return IntStreamPlus.from(stream().filter(predicate));
    }
    
    @Override
    public default IntStreamPlus map(IntUnaryOperator mapper) {
        return IntStreamPlus.from(stream().map(mapper));
    }
    
    public default  Pipeable pipable() {
        return Pipeable.of(this);
    }
    
    public default  T pipe(Function piper) {
        return piper.apply(this);
    }
    
    public default  StreamPlus mapBy(IntFunction mapper) {
        return StreamPlus.from(stream().mapToObj(mapper));
    }
    
    public default StreamPlus asStream() {
        val stream = StreamPlus.from(stream()
                .mapToObj(i -> Integer.valueOf(i)));
        stream.onClose(()->{ close(); });
        return stream;
    }
    
    @Override
    public default  StreamPlus mapToObj(IntFunction mapper) {
        StreamPlus stream = StreamPlus.from(stream().mapToObj(mapper));
        stream.onClose(()->{ close(); });
        return stream;
    }
    
    public default  StreamPlus mapToObj(Supplier supplier) {
        StreamPlus stream = StreamPlus.from(stream().mapToObj(e -> supplier.get()));
        stream.onClose(()->{ close(); });
        return stream;
    }
    
    @Override
    public default LongStreamPlus mapToLong(IntToLongFunction mapper) {
        return LongStreamPlus.from(stream().mapToLong(mapper));
    }
    
    @Override
    public default DoubleStreamPlus mapToDouble(IntToDoubleFunction mapper) {
        return DoubleStreamPlus.from(stream().mapToDouble (mapper));
    }
    
    @Override
    public default IntStreamPlus flatMap(IntFunction mapper) {
        return IntStreamPlus.from(stream().flatMap(mapper));
    }
    
    public default IntStreamPlus flatMap(Function> mapper) {
        return IntStreamPlus.from(stream()
                .mapToObj(i -> Integer.valueOf(i))
                .flatMap (i -> mapper.apply(i))
                .mapToInt(i -> i.intValue()));
    }
    
    @Override
    public default IntStreamPlus distinct() {
        return IntStreamPlus.from(stream().distinct());
    }
    
    @Override
    public default IntStreamPlus sorted() {
        return IntStreamPlus.from(stream().sorted());
    }
    
    @Override
    public default IntStreamPlus peek(IntConsumer action) {
        return IntStreamPlus.from(stream().peek(action));
    }
    
    @Override
    public default IntStreamPlus limit(long maxSize) {
        return IntStreamPlus.from(stream().limit(maxSize));
    }
    
    @Override
    public default IntStreamPlus skip(long n) {
        return IntStreamPlus.from(stream().skip(n));
    }
    
    @Override
    public default void forEach(IntConsumer action) {
        terminate(stream-> {
            stream
            .forEach(action);
        });
    }
    
    @Override
    public default void forEachOrdered(IntConsumer action) {
        terminate(stream-> {
            stream
            .forEachOrdered(action);
        });
    }
    
    @Override
    public default int[] toArray() {
        return terminate(stream-> {
            return stream
                    .toArray();
        });
    }
    
    @Override
    public default int reduce(int identity, IntBinaryOperator op) {
        return terminate(stream-> {
            return stream
                    .reduce(identity, op);
        });
    }
    
    @Override
    public default OptionalInt reduce(IntBinaryOperator op) {
        return terminate(stream-> {
            return stream
                    .reduce(op);
        });
    }
    
    @Override
    public default  R collect(Supplier supplier,
                  ObjIntConsumer accumulator,
                  BiConsumer combiner) {
        return terminate(stream-> {
            return stream
                    .collect(supplier, accumulator, combiner);
        });
    }
    
    @Override
    public default int sum() {
        return terminate(stream-> {
            return stream
                    .sum();
        });
    }
    
    @Override
    public default OptionalInt min() {
        return terminate(stream-> {
            return stream
                    .min();
        });
    }
    
    @Override
    public default OptionalInt max() {
        return terminate(stream-> {
            return stream
                    .max();
        });
    }
    
    @Override
    public default long count() {
        return terminate(stream-> {
            return stream
                    .count();
        });
    }
    
    @Override
    public default OptionalDouble average() {
        return terminate(stream-> {
            return stream
                    .average();
        });
    }
    
    @Override
    public default IntSummaryStatistics summaryStatistics() {
        return terminate(stream-> {
            return stream
                    .summaryStatistics();
        });
    }
    
    @Override
    public default boolean anyMatch(IntPredicate predicate) {
        return terminate(stream-> {
            return stream
                    .anyMatch(predicate);
        });
    }
    
    @Override
    public default boolean allMatch(IntPredicate predicate) {
        return terminate(stream-> {
            return stream
                    .allMatch(predicate);
        });
    }
    
    @Override
    public default boolean noneMatch(IntPredicate predicate) {
        return terminate(stream-> {
            return stream
                    .noneMatch(predicate);
        });
    }
    
    @Override
    public default OptionalInt findFirst() {
        return terminate(stream-> {
            return stream
                    .findFirst();
        });
    }
    
    @Override
    public default OptionalInt findAny() {
        return terminate(stream-> {
            return stream
                    .findAny();
        });
    }
    
    @Override
    public default LongStreamPlus asLongStream() {
        return LongStreamPlus.from(stream().asLongStream());
    }
    
    @Override
    public default DoubleStreamPlus asDoubleStream() {
        return DoubleStreamPlus.from(stream().asDoubleStream());
    }
    
    @Override
    public default StreamPlus boxed() {
        return StreamPlus.from(stream().boxed());
    }
    
    @Override
    public default IntStreamPlus sequential() {
        return IntStreamPlus.from(stream().sequential());
    }
    
    @Override
    public default IntStreamPlus parallel() {
        return IntStreamPlus.from(stream().sequential());
    }
    
    @Override
    public default PrimitiveIterator.OfInt iterator() {
        // TODO - Make sure close is handled properly.
        return stream().iterator();
    }
    
    @Override
    public default Spliterator.OfInt spliterator() {
        return stream().spliterator();
    }
    
    @Override
    public default boolean isParallel() {
        return stream().isParallel();
    }
    
    @Override
    public default IntStreamPlus unordered() {
        return IntStreamPlus.from(stream().unordered());
    }
    
    @Override
    public default IntStreamPlus onClose(Runnable closeHandler) {
        return IntStreamPlus.from(stream().onClose(closeHandler));
    }
    
    @Override
    public default void close() {
        stream().close();
    }
    
    //== Additional functionalities
    
    public default StreamPlus segment(int count) {
        return segment(count, true);
    }
    public default StreamPlus segment(int count, boolean includeTail) {
//        if (count <= 1)
//            return this;
        
        val index = new AtomicInteger(0);
        return segment(data -> (index.getAndIncrement() % count) == 0, includeTail);
    }
    public default StreamPlus segment(IntPredicate startCondition) {
        return segment(startCondition, true);
    }
    public default StreamPlus segment(IntPredicate startCondition, boolean includeTail) {
        val list = new AtomicReference<>(new ArrayList());
        val adding = new AtomicBoolean(false);
        
        StreamPlus mainStream = StreamPlus.from(
                mapToObj(i ->{
                    if (startCondition.test(i)) {
                        adding.set(true);
                        val retList = list.getAndUpdate(l -> new ArrayList());
                        list.get().add(i);
                        
                        if (retList.isEmpty())
                            return null;
                        
                        return IntStreamPlus.from(retList.stream().mapToInt(Integer::intValue));
                    }
                    if (adding.get()) list.get().add(i);
                    return null;
                }))
                .filterNonNull();
        ;
        val mainSupplier = (Supplier>)()->mainStream;
        if (!includeTail)
            return mainStream;
        
        val tailSupplier = (Supplier>)()->{
            return StreamPlus.of(
                    IntStreamPlus.from(
                            list.get()
                            .stream()
                            .mapToInt(Integer::intValue)));
        };
        val resultStream = StreamPlus.of(
            mainSupplier,
            tailSupplier
        )
        .flatMap(Supplier::get);
        return resultStream;
    }
    
    public default StreamPlus segment(IntPredicate startCondition, IntPredicate endCondition) {
        return segment(startCondition, endCondition, true);
    }
    
    public default StreamPlus segment(IntPredicate startCondition, IntPredicate endCondition, boolean includeTail) {
        val list = new AtomicReference<>(new ArrayList());
        val adding = new AtomicBoolean(false);
        
        StreamPlus stream = StreamPlus.from(
                mapToObj(i ->{
                    if (startCondition.test(i)) {
                        adding.set(true);
                    }
                    if (includeTail && adding.get()) list.get().add(i);
                    if (endCondition.test(i)) {
                        adding.set(false);
                        val retList = list.getAndUpdate(l -> new ArrayList());
                        return IntStreamPlus.from(retList.stream().mapToInt(Integer::intValue));
                    }
                    
                    if (!includeTail && adding.get()) list.get().add(i);
                    return null;
                }))
            .filterNonNull();
        return stream;
    }
    
    // TODO - percentile (multiple percentile value) and percentileRange (invert of percentile)
    // TODO - segmentAt, segmentAtPercentiles
    
    public default List toList() {
        return asStream().toJavaList();
    }
    
    public default FuncList toFuncList() {
        return asStream().toImmutableList();
    }
    
    public default ImmutableList toImmutableList() {
        return asStream().toImmutableList();
    }
    
    public default List toMutableList() {
        return asStream().toMutableList();
    }
    
    public default ArrayList toArrayList() {
        return asStream().toArrayList();
    }
    
    public default Set toSet() {
        return asStream().toSet();
    }
    
    //== Plus ==
    
    public default String joining() {
        return terminate(stream -> {
            return stream()
                .mapToObj(StrFuncs::toStr)
                .collect(Collectors.joining());
        });
    }
    public default String joining(String delimiter) {
        return terminate(stream -> {
            return stream()
                .mapToObj(StrFuncs::toStr)
                .collect(Collectors.joining(delimiter));
        });
    }
    
    //== Get ==
    
    public default  T get(IntStreamElementProcessor processor) {
        val counter = new AtomicLong(0);
        val iterator = iterator();
        while (iterator.hasNext()) {
            val each  = iterator.nextInt();
            val index = counter.getAndIncrement();
            processor.processElement(index, each);
        }
        val count = counter.get();
        return processor.processComplete(count);
    }
    
    public default  Tuple2 get(
                IntStreamElementProcessor processor1, 
                IntStreamElementProcessor processor2) {
        return get(processor1, processor2, Tuple2::of);
    }
    
    public default  T get(
                IntStreamElementProcessor processor1, 
                IntStreamElementProcessor processor2,
                Func2          combiner) {
        val counter = new AtomicLong(0);
        val iterator = iterator();
        while (iterator.hasNext()) {
            val each  = iterator.nextInt();
            val index = counter.getAndIncrement();
            processor1.processElement(index, each);
            processor2.processElement(index, each);
        }
        val count = counter.get();
        val value1 = processor1.processComplete(count);
        val value2 = processor2.processComplete(count);
        return combiner.apply(value1, value2);
    }
    
    public default  Tuple3 get(
                IntStreamElementProcessor processor1, 
                IntStreamElementProcessor processor2, 
                IntStreamElementProcessor processor3) {
        return get(processor1, processor2, processor3, Tuple3::of);
    }
    
    public default  T get(
                IntStreamElementProcessor processor1, 
                IntStreamElementProcessor processor2, 
                IntStreamElementProcessor processor3,
                Func3   combiner) {
        val counter = new AtomicLong(0);
        val iterator = iterator();
        while (iterator.hasNext()) {
            val each  = iterator.nextInt();
            val index = counter.getAndIncrement();
            processor1.processElement(index, each);
            processor2.processElement(index, each);
            processor3.processElement(index, each);
        }
        val count = counter.get();
        val value1 = processor1.processComplete(count);
        val value2 = processor2.processComplete(count);
        val value3 = processor3.processComplete(count);
        return combiner.apply(value1, value2, value3);
    }
    
    public default  Tuple4 get(
                IntStreamElementProcessor processor1, 
                IntStreamElementProcessor processor2, 
                IntStreamElementProcessor processor3, 
                IntStreamElementProcessor processor4) {
        return get(processor1, processor2, processor3, processor4, Tuple4::of);
    }
    
    public default  T get(
                IntStreamElementProcessor processor1, 
                IntStreamElementProcessor processor2, 
                IntStreamElementProcessor processor3,
                IntStreamElementProcessor processor4,
                Func4  combiner) {
        val counter = new AtomicLong(0);
        val iterator = iterator();
        while (iterator.hasNext()) {
            val each  = iterator.nextInt();
            val index = counter.getAndIncrement();
            processor1.processElement(index, each);
            processor2.processElement(index, each);
            processor3.processElement(index, each);
            processor4.processElement(index, each);
        }
        val count = counter.get();
        val value1 = processor1.processComplete(count);
        val value2 = processor2.processComplete(count);
        val value3 = processor3.processComplete(count);
        val value4 = processor4.processComplete(count);
        return combiner.apply(value1, value2, value3, value4);
    }
    
    public default  Tuple5 get(
                IntStreamElementProcessor processor1, 
                IntStreamElementProcessor processor2, 
                IntStreamElementProcessor processor3, 
                IntStreamElementProcessor processor4, 
                IntStreamElementProcessor processor5) {
        return get(processor1, processor2, processor3, processor4, processor5, Tuple5::of);
    }
    
    public default  T get(
                IntStreamElementProcessor processor1, 
                IntStreamElementProcessor processor2, 
                IntStreamElementProcessor processor3,
                IntStreamElementProcessor processor4,
                IntStreamElementProcessor processor5,
                Func5  combiner) {
        val counter = new AtomicLong(0);
        val iterator = iterator();
        while (iterator.hasNext()) {
            val each  = iterator.nextInt();
            val index = counter.getAndIncrement();
            processor1.processElement(index, each);
            processor2.processElement(index, each);
            processor3.processElement(index, each);
            processor4.processElement(index, each);
            processor5.processElement(index, each);
        }
        val count = counter.get();
        val value1 = processor1.processComplete(count);
        val value2 = processor2.processComplete(count);
        val value3 = processor3.processComplete(count);
        val value4 = processor4.processComplete(count);
        val value5 = processor5.processComplete(count);
        return combiner.apply(value1, value2, value3, value4, value5);
    }
    
    public default  Tuple6 get(
                IntStreamElementProcessor processor1, 
                IntStreamElementProcessor processor2, 
                IntStreamElementProcessor processor3, 
                IntStreamElementProcessor processor4, 
                IntStreamElementProcessor processor5, 
                IntStreamElementProcessor processor6) {
        return get(processor1, processor2, processor3, processor4, processor5, processor6, Tuple6::of);
    }
    
    public default  T get(
                IntStreamElementProcessor processor1, 
                IntStreamElementProcessor processor2, 
                IntStreamElementProcessor processor3,
                IntStreamElementProcessor processor4,
                IntStreamElementProcessor processor5,
                IntStreamElementProcessor processor6,
                Func6  combiner) {
        val counter = new AtomicLong(0);
        val iterator = iterator();
        while (iterator.hasNext()) {
            val each  = iterator.nextInt();
            val index = counter.getAndIncrement();
            processor1.processElement(index, each);
            processor2.processElement(index, each);
            processor3.processElement(index, each);
            processor4.processElement(index, each);
            processor5.processElement(index, each);
            processor6.processElement(index, each);
        }
        val count = counter.get();
        val value1 = processor1.processComplete(count);
        val value2 = processor2.processComplete(count);
        val value3 = processor3.processComplete(count);
        val value4 = processor4.processComplete(count);
        val value5 = processor5.processComplete(count);
        val value6 = processor6.processComplete(count);
        return combiner.apply(value1, value2, value3, value4, value5, value6);
    }
}