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

com.landawn.abacus.util.stream.AbstractLongStream Maven / Gradle / Ivy

Go to download

A general programming library in Java/Android. It's easy to learn and simple to use with concise and powerful APIs.

There is a newer version: 5.2.4
Show newest version
/*
 * Copyright (C) 2016, 2017, 2018, 2019 HaiYang Li
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.landawn.abacus.util.stream;

import java.util.Collection;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.LongBinaryOperator;
import java.util.function.LongConsumer;
import java.util.function.LongFunction;
import java.util.function.LongPredicate;
import java.util.function.ObjLongConsumer;
import java.util.function.Supplier;
import java.util.stream.Collector;

import com.landawn.abacus.exception.TooManyElementsException;
import com.landawn.abacus.util.Fn;
import com.landawn.abacus.util.Fn.Suppliers;
import com.landawn.abacus.util.IndexedLong;
import com.landawn.abacus.util.Joiner;
import com.landawn.abacus.util.LongIterator;
import com.landawn.abacus.util.LongList;
import com.landawn.abacus.util.LongSummaryStatistics;
import com.landawn.abacus.util.MergeResult;
import com.landawn.abacus.util.Multiset;
import com.landawn.abacus.util.MutableInt;
import com.landawn.abacus.util.MutableLong;
import com.landawn.abacus.util.N;
import com.landawn.abacus.util.Pair;
import com.landawn.abacus.util.Percentage;
import com.landawn.abacus.util.Strings.StringUtil;
import com.landawn.abacus.util.Throwables;
import com.landawn.abacus.util.Tuple.Tuple3;
import com.landawn.abacus.util.u.Optional;
import com.landawn.abacus.util.u.OptionalLong;
import com.landawn.abacus.util.function.LongBiFunction;
import com.landawn.abacus.util.function.LongBiPredicate;
import com.landawn.abacus.util.function.LongMapMultiConsumer;
import com.landawn.abacus.util.function.LongTernaryOperator;
import com.landawn.abacus.util.function.LongTriPredicate;

/**
 *
 */
abstract class AbstractLongStream extends LongStream {

    AbstractLongStream(final boolean sorted, final Collection closeHandlers) {
        super(sorted, closeHandlers);
    }

    @Override
    public LongStream distinct() {
        assertNotClosed();

        final Set set = N.newHashSet();

        return newStream(this.sequential().filter(set::add).iteratorEx(), sorted);
    }

    //    @Override
    //    public LongStream flatmap(final LongFunction mapper) {
    //        assertNotClosed();
    //
    //        return flatMap(new LongFunction() {
    //            @Override
    //            public LongStream apply(long t) {
    //                return LongStream.of(mapper.apply(t));
    //            }
    //        });
    //    }

    @Override
    public LongStream flatmap(final LongFunction mapper) {
        assertNotClosed();

        return flatMap(t -> LongStream.of(mapper.apply(t)));
    }

    @Override
    public  Stream flatmapToObj(final LongFunction> mapper) {
        assertNotClosed();

        return flatMapToObj(t -> Stream.of(mapper.apply(t)));
    }

    @Override
    public  Stream flattMapToObj(final LongFunction mapper) {
        assertNotClosed();

        return flatMapToObj(t -> Stream.of(mapper.apply(t)));
    }

    @Override
    public LongStream mapMulti(final LongMapMultiConsumer mapper) {
        final LongFunction mapper2 = t -> {
            final SpinedBuffer.OfLong buffer = new SpinedBuffer.OfLong();

            mapper.accept(t, buffer);

            return LongStream.of(buffer.iterator());
        };

        return flatMap(mapper2);
    }

    @Override
    public LongStream mapPartial(final LongFunction mapper) {
        if (isParallel()) {
            return mapToObj(mapper).psp(s -> s.filter(Fn.IS_PRESENT_LONG).mapToLong(Fn.GET_AS_LONG));
        } else {
            return mapToObj(mapper).filter(Fn.IS_PRESENT_LONG).mapToLong(Fn.GET_AS_LONG);
        }
    }

    @Override
    public LongStream mapPartialJdk(final LongFunction mapper) {
        if (isParallel()) {
            return mapToObj(mapper).psp(s -> s.filter(Fn.IS_PRESENT_LONG_JDK).mapToLong(Fn.GET_AS_LONG_JDK));
        } else {
            return mapToObj(mapper).filter(Fn.IS_PRESENT_LONG_JDK).mapToLong(Fn.GET_AS_LONG_JDK);
        }
    }

    @Override
    public LongStream rangeMap(final LongBiPredicate sameRange, final LongBinaryOperator mapper) {
        assertNotClosed();

        final LongIteratorEx iter = iteratorEx();

        return newStream(new LongIteratorEx() { //NOSONAR
            private long left = 0, right = 0, next = 0;
            private boolean hasNext = false;

            @Override
            public boolean hasNext() {
                return hasNext || iter.hasNext();
            }

            @Override
            public long nextLong() {
                left = hasNext ? next : iter.nextLong();
                right = left;

                while (hasNext = iter.hasNext()) {
                    next = iter.nextLong();

                    if (sameRange.test(left, next)) {
                        right = next;
                    } else {
                        break;
                    }
                }

                return mapper.applyAsLong(left, right);
            }
        }, false);
    }

    @Override
    public  Stream rangeMapToObj(final LongBiPredicate sameRange, final LongBiFunction mapper) {
        assertNotClosed();

        final LongIteratorEx iter = iteratorEx();

        return newStream(new ObjIteratorEx() { //NOSONAR
            private long left = 0, right = 0, next = 0;
            private boolean hasNext = false;

            @Override
            public boolean hasNext() {
                return hasNext || iter.hasNext();
            }

            @Override
            public T next() {
                left = hasNext ? next : iter.nextLong();
                right = left;

                while (hasNext = iter.hasNext()) {
                    next = iter.nextLong();

                    if (sameRange.test(left, next)) {
                        right = next;
                    } else {
                        break;
                    }
                }

                return mapper.apply(left, right);
            }
        }, false, null);
    }

    @Override
    public Stream collapse(final LongBiPredicate collapsible) {
        assertNotClosed();

        final LongIteratorEx iter = iteratorEx();

        return newStream(new ObjIteratorEx() { //NOSONAR
            private boolean hasNext = false;
            private long next = 0;

            @Override
            public boolean hasNext() {
                return hasNext || iter.hasNext();
            }

            @Override
            public LongList next() {
                final LongList result = new LongList(9);
                result.add(hasNext ? next : (next = iter.nextLong()));

                while ((hasNext = iter.hasNext())) {
                    if (collapsible.test(next, (next = iter.nextLong()))) {
                        result.add(next);
                    } else {
                        break;
                    }
                }

                return result;
            }
        }, false, null);
    }

    @Override
    public LongStream collapse(final LongBiPredicate collapsible, final LongBinaryOperator mergeFunction) {
        assertNotClosed();

        final LongIteratorEx iter = iteratorEx();

        return newStream(new LongIteratorEx() { //NOSONAR
            private boolean hasNext = false;
            private long next = 0;

            @Override
            public boolean hasNext() {
                return hasNext || iter.hasNext();
            }

            @Override
            public long nextLong() {
                long res = hasNext ? next : (next = iter.nextLong());

                while ((hasNext = iter.hasNext())) {
                    if (collapsible.test(next, (next = iter.nextLong()))) {
                        res = mergeFunction.applyAsLong(res, next);
                    } else {
                        break;
                    }
                }

                return res;
            }
        }, false);
    }

    @Override
    public LongStream collapse(final LongTriPredicate collapsible, final LongBinaryOperator mergeFunction) {
        assertNotClosed();

        final LongIteratorEx iter = iteratorEx();

        return newStream(new LongIteratorEx() { //NOSONAR
            private boolean hasNext = false;
            private long next = 0;

            @Override
            public boolean hasNext() {
                return hasNext || iter.hasNext();
            }

            @Override
            public long nextLong() {
                final long first = hasNext ? next : (next = iter.nextLong());
                long res = first;

                while ((hasNext = iter.hasNext())) {
                    if (collapsible.test(first, next, (next = iter.nextLong()))) {
                        res = mergeFunction.applyAsLong(res, next);
                    } else {
                        break;
                    }
                }

                return res;
            }
        }, false);
    }

    @Override
    public LongStream skip(final long n, final LongConsumer action) {
        assertNotClosed();

        checkArgNotNegative(n, "n");
        checkArgNotNull(action, "action");

        final LongPredicate filter = isParallel() ? new LongPredicate() {
            final AtomicLong cnt = new AtomicLong(n);

            @Override
            public boolean test(long value) {
                return cnt.getAndDecrement() > 0;
            }
        } : new LongPredicate() {
            final MutableLong cnt = MutableLong.of(n);

            @Override
            public boolean test(long value) {
                return cnt.getAndDecrement() > 0;
            }
        };

        return dropWhile(filter, action);
    }

    //    @Override
    //    public LongStream removeIf(final LongPredicate predicate) {
    //        assertNotClosed();
    //
    //        return filter(value -> !predicate.test(value));
    //    }
    //
    //    @Override
    //    public LongStream removeIf(final LongPredicate predicate, final LongConsumer actionOnDroppedItem) {
    //        assertNotClosed();
    //
    //        return filter(value -> {
    //            if (predicate.test(value)) {
    //                actionOnDroppedItem.accept(value);
    //                return false;
    //            }
    //
    //            return true;
    //        });
    //    }

    @Override
    public LongStream filter(final LongPredicate predicate, final LongConsumer actionOnDroppedItem) {
        assertNotClosed();

        return filter(value -> {
            if (!predicate.test(value)) {
                actionOnDroppedItem.accept(value);
                return false;
            }

            return true;
        });
    }

    @Override
    public LongStream dropWhile(final LongPredicate predicate, final LongConsumer actionOnDroppedItem) {
        assertNotClosed();

        return dropWhile(value -> {
            if (predicate.test(value)) {
                actionOnDroppedItem.accept(value);
                return true;
            }

            return false;
        });
    }

    @Override
    public LongStream step(final long step) {
        assertNotClosed();

        checkArgPositive(step, "step");

        if (step == 1) {
            return skip(0);
        }

        final long skip = step - 1;
        final LongIteratorEx iter = this.iteratorEx();

        final LongIterator longIterator = new LongIteratorEx() {
            @Override
            public boolean hasNext() {
                return iter.hasNext();
            }

            @Override
            public long nextLong() {
                final long next = iter.nextLong();
                iter.advance(skip);
                return next;
            }
        };

        return newStream(longIterator, sorted);
    }

    @Override
    public Stream split(final int chunkSize) {
        assertNotClosed();

        return splitToList(chunkSize).map(t -> new ArrayLongStream(t.array(), 0, t.size(), sorted, null));
    }

    @Override
    public Stream split(final LongPredicate predicate) {
        assertNotClosed();

        return splitToList(predicate).map(t -> new ArrayLongStream(t.array(), 0, t.size(), sorted, null));
    }

    @Override
    public Stream splitAt(final LongPredicate where) {
        assertNotClosed();

        final LongIteratorEx iter = iteratorEx();

        return newStream(new ObjIteratorEx() { //NOSONAR
            private int cursor = 0;
            private long next = 0;
            private boolean hasNext = false;

            @Override
            public boolean hasNext() {
                return cursor < 2;
            }

            @Override
            public LongStream next() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }

                LongStream result = null;

                if (cursor == 0) {
                    final LongList list = new LongList();

                    while (iter.hasNext()) {
                        next = iter.nextLong();

                        if (!where.test(next)) {
                            list.add(next);
                        } else {
                            hasNext = true;
                            break;
                        }
                    }

                    result = new ArrayLongStream(list.array(), 0, list.size(), sorted, null);
                } else {
                    LongIteratorEx iterEx = iter;

                    if (hasNext) {
                        iterEx = new LongIteratorEx() {
                            private boolean isFirst = true;

                            @Override
                            public boolean hasNext() {
                                return isFirst || iter.hasNext();
                            }

                            @Override
                            public long nextLong() {
                                if (!hasNext()) {
                                    throw new NoSuchElementException();
                                }

                                if (isFirst) {
                                    isFirst = false;
                                    return next;
                                } else {
                                    return iter.nextLong();
                                }
                            }
                        };
                    }

                    result = new IteratorLongStream(iterEx, sorted, null);
                }

                cursor++;

                return result;
            }

            @Override
            public long count() {
                iter.count();

                return 2 - cursor; //NOSONAR
            }

            @Override
            public void advance(long n) {
                if (n == 0) {
                    return;
                } else if (n == 1) {
                    if (cursor == 0) {
                        while (iter.hasNext()) {
                            next = iter.nextLong();

                            if (!where.test(next)) {
                                hasNext = true;
                                break;
                            }
                        }
                    } else {
                        iter.advance(Long.MAX_VALUE);
                    }
                } else {
                    iter.advance(Long.MAX_VALUE);
                }

                cursor = n >= 2 ? 2 : cursor + (int) n;
            }

        }, false, null);
    }

    @Override
    public Stream sliding(final int windowSize, final int increment) {
        assertNotClosed();

        return slidingToList(windowSize, increment).map(t -> new ArrayLongStream(t.array(), 0, t.size(), sorted, null));
    }

    @Override
    public LongStream scan(final LongBinaryOperator accumulator) {
        assertNotClosed();

        final LongIteratorEx iter = iteratorEx();

        return newStream(new LongIteratorEx() { //NOSONAR
            private long res = 0;
            private boolean isFirst = true;

            @Override
            public boolean hasNext() {
                return iter.hasNext();
            }

            @Override
            public long nextLong() {
                if (isFirst) {
                    isFirst = false;
                    return (res = iter.nextLong());
                } else {
                    return (res = accumulator.applyAsLong(res, iter.nextLong()));
                }
            }
        }, false);
    }

    @Override
    public LongStream scan(final long init, final LongBinaryOperator accumulator) {
        assertNotClosed();

        final LongIteratorEx iter = iteratorEx();

        return newStream(new LongIteratorEx() { //NOSONAR
            private long res = init;

            @Override
            public boolean hasNext() {
                return iter.hasNext();
            }

            @Override
            public long nextLong() {
                return (res = accumulator.applyAsLong(res, iter.nextLong()));
            }
        }, false);
    }

    @Override
    public LongStream scan(final long init, final LongBinaryOperator accumulator, final boolean initIncluded) {
        assertNotClosed();

        if (!initIncluded) {
            return scan(init, accumulator);
        }

        final LongIteratorEx iter = iteratorEx();

        return newStream(new LongIteratorEx() { //NOSONAR
            private boolean isFirst = true;
            private long res = init;

            @Override
            public boolean hasNext() {
                return isFirst || iter.hasNext();
            }

            @Override
            public long nextLong() {
                if (isFirst) {
                    isFirst = false;
                    return init;
                }

                return (res = accumulator.applyAsLong(res, iter.nextLong()));
            }
        }, false);
    }

    @Override
    public LongStream top(int n) {
        assertNotClosed();

        return top(n, LONG_COMPARATOR);
    }

    @Override
    public LongStream intersection(final Collection c) {
        assertNotClosed();

        final Multiset multiset = Multiset.from(c);

        return newStream(this.sequential().filter(value -> multiset.getAndRemove(value) > 0).iteratorEx(), sorted);
    }

    @Override
    public LongStream difference(final Collection c) {
        assertNotClosed();

        final Multiset multiset = Multiset.from(c);

        return newStream(this.sequential().filter(value -> multiset.getAndRemove(value) < 1).iteratorEx(), sorted);
    }

    @Override
    public LongStream symmetricDifference(final Collection c) {
        assertNotClosed();

        final Multiset multiset = Multiset.from(c);

        return newStream(this.sequential()
                .filter(value -> multiset.getAndRemove(value) < 1)
                .append(Stream.of(c).filter(value -> multiset.getAndRemove(value) > 0).mapToLong(com.landawn.abacus.util.function.ToLongFunction.UNBOX))
                .iteratorEx(), false);
    }

    @Override
    public LongStream reversed() {
        assertNotClosed();

        return newStream(new LongIteratorEx() { //NOSONAR
            private boolean initialized = false;

            private long[] elements;
            private int fromIndex = -1;
            private int toIndex = -1;

            private int cursor;

            @Override
            public boolean hasNext() {
                if (!initialized) {
                    init();
                }

                return cursor > fromIndex;
            }

            @Override
            public long nextLong() {
                if (!initialized) {
                    init();
                }

                if (cursor <= fromIndex) {
                    throw new NoSuchElementException();
                }

                return elements[--cursor];
            }

            @Override
            public long count() {
                if (!initialized) {
                    init();
                }

                return cursor - fromIndex; //NOSONAR
            }

            @Override
            public void advance(long n) {
                if (!initialized) {
                    init();
                }

                cursor = n < cursor - fromIndex ? cursor - (int) n : fromIndex;
            }

            @Override
            public long[] toArray() {
                if (!initialized) {
                    init();
                }

                final long[] a = new long[cursor - fromIndex];

                for (int i = 0, len = cursor - fromIndex; i < len; i++) {
                    a[i] = elements[cursor - i - 1];
                }

                return a;
            }

            private void init() {
                if (!initialized) {
                    initialized = true;

                    final Tuple3 tp = AbstractLongStream.this.arrayForIntermediateOp();

                    elements = tp._1;
                    fromIndex = tp._2;
                    toIndex = tp._3;

                    cursor = toIndex;
                }
            }
        }, false);
    }

    @Override
    public LongStream rotated(final int distance) {
        assertNotClosed();

        return newStream(new LongIteratorEx() { //NOSONAR
            private boolean initialized = false;

            private long[] elements;
            private int fromIndex = -1;
            private int toIndex = -1;

            private int len;
            private int start;
            private int cnt = 0;

            @Override
            public boolean hasNext() {
                if (!initialized) {
                    init();
                }

                return cnt < len;
            }

            @Override
            public long nextLong() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }

                return elements[((start + cnt++) % len) + fromIndex];
            }

            @Override
            public long count() {
                if (!initialized) {
                    init();
                }

                return len - cnt; //NOSONAR
            }

            @Override
            public void advance(long n) {
                if (!initialized) {
                    init();
                }

                cnt = n < len - cnt ? cnt + (int) n : len;
            }

            @Override
            public long[] toArray() {
                if (!initialized) {
                    init();
                }

                final long[] a = new long[len - cnt];

                for (int i = cnt; i < len; i++) {
                    a[i - cnt] = elements[((start + i) % len) + fromIndex];
                }

                return a;
            }

            private void init() {
                if (!initialized) {
                    initialized = true;

                    final Tuple3 tp = AbstractLongStream.this.arrayForIntermediateOp();

                    elements = tp._1;
                    fromIndex = tp._2;
                    toIndex = tp._3;

                    len = toIndex - fromIndex;

                    if (len > 0) {
                        start = distance % len;

                        if (start < 0) {
                            start += len;
                        }

                        start = len - start;
                    }
                }
            }
        }, distance == 0 && sorted);
    }

    @Override
    public LongStream shuffled(final Random rnd) {
        assertNotClosed();

        checkArgNotNull(rnd, "random");

        return lazyLoad(a -> {
            N.shuffle(a, rnd);
            return a;
        }, false);
    }

    @Override
    public LongStream sorted() {
        assertNotClosed();

        if (sorted) {
            return newStream(iteratorEx(), sorted);
        }

        return lazyLoad(a -> {
            if (isParallel()) {
                N.parallelSort(a);
            } else {
                N.sort(a);
            }

            return a;
        }, true);
    }

    @Override
    public LongStream reverseSorted() {
        assertNotClosed();

        return newStream(new LongIteratorEx() { //NOSONAR
            private boolean initialized = false;
            private long[] aar;
            private int cursor;

            @Override
            public boolean hasNext() {
                if (!initialized) {
                    init();
                }

                return cursor > 0;
            }

            @Override
            public long nextLong() {
                if (!initialized) {
                    init();
                }

                if (cursor <= 0) {
                    throw new NoSuchElementException();
                }

                return aar[--cursor];
            }

            @Override
            public long count() {
                if (!initialized) {
                    init();
                }

                return cursor;
            }

            @Override
            public void advance(long n) {
                if (!initialized) {
                    init();
                }

                cursor = n < cursor ? cursor - (int) n : 0;
            }

            @Override
            public long[] toArray() {
                if (!initialized) {
                    init();
                }

                final long[] a = new long[cursor];

                for (int i = 0; i < cursor; i++) {
                    a[i] = aar[cursor - i - 1];
                }

                return a;
            }

            private void init() {
                if (!initialized) {
                    initialized = true;
                    aar = AbstractLongStream.this.toArrayForIntermediateOp();

                    if (isParallel()) {
                        N.parallelSort(aar);
                    } else {
                        N.sort(aar);
                    }

                    cursor = aar.length;
                }
            }
        }, false);
    }

    private LongStream lazyLoad(final Function op, final boolean sorted) {
        return newStream(new LongIteratorEx() { //NOSONAR
            private boolean initialized = false;
            private long[] aar;
            private int cursor = 0;
            private int len;

            @Override
            public boolean hasNext() {
                if (!initialized) {
                    init();
                }

                return cursor < len;
            }

            @Override
            public long nextLong() {
                if (!initialized) {
                    init();
                }

                if (cursor >= len) {
                    throw new NoSuchElementException();
                }

                return aar[cursor++];
            }

            @Override
            public long count() {
                if (!initialized) {
                    init();
                }

                return len - cursor; //NOSONAR
            }

            @Override
            public void advance(long n) {
                if (!initialized) {
                    init();
                }

                cursor = n > len - cursor ? len : cursor + (int) n;
            }

            @Override
            public long[] toArray() {
                if (!initialized) {
                    init();
                }

                final long[] a = new long[len - cursor];

                for (int i = cursor; i < len; i++) {
                    a[i - cursor] = aar[i];
                }

                return a;
            }

            private void init() {
                if (!initialized) {
                    initialized = true;
                    aar = op.apply(AbstractLongStream.this.toArrayForIntermediateOp());
                    len = aar.length;
                }
            }
        }, sorted);
    }

    @Override
    public LongStream cycled() {
        assertNotClosed();

        return newStream(new LongIterator() { //NOSONAR
            private LongIterator iter = null;
            private LongList list = null;
            private long[] a = null;
            private int len = 0;
            private int cursor = -1;
            private long e = 0;

            private boolean initialized = false;

            @Override
            public boolean hasNext() {
                if (!initialized) {
                    init();
                }

                if (a == null && !iter.hasNext()) {
                    a = list.toArray();
                    len = a.length;
                    cursor = 0;
                }

                return cursor < len || iter.hasNext();
            }

            @Override
            public long nextLong() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }

                if (len > 0) {
                    if (cursor >= len) {
                        cursor = 0;
                    }

                    return a[cursor++];
                } else {
                    e = iter.nextLong();
                    list.add(e);

                    return e;
                }
            }

            private void init() {
                if (!initialized) {
                    initialized = true;
                    iter = AbstractLongStream.this.iteratorEx();
                    list = new LongList();
                }
            }
        }, false);
    }

    @Override
    public LongStream cycled(final long rounds) {
        assertNotClosed();

        checkArgNotNegative(rounds, "times");

        if (rounds == 0) {
            return limit(0);
        } else if (rounds == 1) {
            return skip(0);
        }

        return newStream(new LongIterator() { //NOSONAR
            private LongIterator iter = null;
            private LongList list = null;
            private long[] a = null;
            private int len = 0;
            private int cursor = -1;
            private long e = 0;
            private long m = 0;

            private boolean initialized = false;

            @Override
            public boolean hasNext() {
                if (!initialized) {
                    init();
                }

                if (a == null && !iter.hasNext()) {
                    a = list.toArray();
                    len = a.length;
                    cursor = 0;
                    m = 1;
                }

                return m < rounds && (cursor < len || rounds - m > 1) && (len > 0 || iter.hasNext());
            }

            @Override
            public long nextLong() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }

                if (len > 0) {
                    if (cursor >= len) {
                        cursor = 0;
                        m++;
                    }

                    return a[cursor++];
                } else {
                    e = iter.nextLong();
                    list.add(e);

                    return e;
                }
            }

            private void init() {
                if (!initialized) {
                    initialized = true;
                    iter = AbstractLongStream.this.iteratorEx();
                    list = new LongList();
                }
            }
        }, rounds <= 1 ? this.sorted : false);
    }

    @Override
    public Stream indexed() {
        assertNotClosed();

        final MutableLong idx = MutableLong.of(0);

        return newStream(this.sequential().mapToObj(t -> IndexedLong.of(t, idx.getAndIncrement())).iteratorEx(), true, INDEXED_LONG_COMPARATOR);
    }

    @Override
    public Stream boxed() {
        assertNotClosed();

        return newStream(iteratorEx(), sorted, sorted ? LONG_COMPARATOR : null);
    }

    @Override
    @SafeVarargs
    public final LongStream prepend(final long... a) {
        assertNotClosed();

        return prepend(LongStream.of(a));
    }

    @Override
    public LongStream prepend(LongStream stream) {
        assertNotClosed();

        if (isParallel()) {
            return LongStream.concat(stream, this)
                    .parallel(maxThreadNum(), executorNumForVirtualThread(), splitor(), asyncExecutor(), cancelUncompletedThreads());
        } else {
            return LongStream.concat(stream, this);
        }
    }

    @Override
    public LongStream prepend(final OptionalLong op) {
        assertNotClosed();

        return prepend(op.stream());
    }

    @Override
    @SafeVarargs
    public final LongStream append(final long... a) {
        assertNotClosed();

        return append(LongStream.of(a));
    }

    @Override
    public LongStream append(LongStream stream) {
        assertNotClosed();

        if (isParallel()) {
            return LongStream.concat(this, stream)
                    .parallel(maxThreadNum(), executorNumForVirtualThread(), splitor(), asyncExecutor(), cancelUncompletedThreads());
        } else {
            return LongStream.concat(this, stream);
        }
    }

    @Override
    public LongStream append(final OptionalLong op) { //NOSONAR
        assertNotClosed();

        return prepend(op.stream());
    }

    @Override
    @SafeVarargs
    public final LongStream appendIfEmpty(final long... a) {
        assertNotClosed();

        return appendIfEmpty(() -> LongStream.of(a));
    }

    @Override
    public LongStream mergeWith(LongStream b, LongBiFunction nextSelector) {
        assertNotClosed();

        if (isParallel()) {
            return LongStream.merge(this, b, nextSelector)
                    .parallel(maxThreadNum(), executorNumForVirtualThread(), splitor(), asyncExecutor(), cancelUncompletedThreads());
        } else {
            return LongStream.merge(this, b, nextSelector);
        }
    }

    @Override
    public LongStream zipWith(LongStream b, LongBinaryOperator zipFunction) {
        assertNotClosed();

        return LongStream.zip(this, b, zipFunction);
    }

    @Override
    public LongStream zipWith(LongStream b, LongStream c, LongTernaryOperator zipFunction) {
        assertNotClosed();

        return LongStream.zip(this, b, c, zipFunction);
    }

    @Override
    public LongStream zipWith(LongStream b, long valueForNoneA, long valueForNoneB, LongBinaryOperator zipFunction) {
        assertNotClosed();

        return LongStream.zip(this, b, valueForNoneA, valueForNoneB, zipFunction);
    }

    @Override
    public LongStream zipWith(LongStream b, LongStream c, long valueForNoneA, long valueForNoneB, long valueForNoneC, LongTernaryOperator zipFunction) {
        assertNotClosed();

        return LongStream.zip(this, b, c, valueForNoneA, valueForNoneB, valueForNoneC, zipFunction);
    }

    //    @Override
    //    public LongStream cached() {
    //        return newStream(toArray(), sorted);
    //    }

    @Override
    public  Map toMap(Throwables.LongFunction keyMapper,
            Throwables.LongFunction valueMapper) throws E, E2 {
        assertNotClosed();

        return toMap(keyMapper, valueMapper, Suppliers. ofMap());
    }

    @Override
    public , E extends Exception, E2 extends Exception> M toMap(Throwables.LongFunction keyMapper,
            Throwables.LongFunction valueMapper, Supplier mapFactory) throws E, E2 {
        assertNotClosed();

        return toMap(keyMapper, valueMapper, Fn. throwingMerger(), mapFactory);
    }

    @Override
    public  Map toMap(Throwables.LongFunction keyMapper,
            Throwables.LongFunction valueMapper, BinaryOperator mergeFunction) throws E, E2 {
        assertNotClosed();

        return toMap(keyMapper, valueMapper, mergeFunction, Suppliers. ofMap());
    }

    @Override
    public  Map groupTo(Throwables.LongFunction keyMapper, final Collector downstream)
            throws E {
        assertNotClosed();

        return groupTo(keyMapper, downstream, Suppliers. ofMap());
    }

    @Override
    public  void forEachIndexed(Throwables.IndexedLongConsumer action) throws E {
        assertNotClosed();

        if (isParallel()) {
            final AtomicInteger idx = new AtomicInteger(0);

            forEach(t -> action.accept(idx.getAndIncrement(), t));
        } else {
            final MutableInt idx = MutableInt.of(0);

            forEach(t -> action.accept(idx.getAndIncrement(), t));
        }
    }

    @Override
    public OptionalLong first() {
        assertNotClosed();

        try {
            final LongIterator iter = this.iteratorEx();

            return iter.hasNext() ? OptionalLong.of(iter.nextLong()) : OptionalLong.empty();
        } finally {
            close();
        }
    }

    @Override
    public OptionalLong last() {
        assertNotClosed();

        try {
            final LongIterator iter = this.iteratorEx();

            if (!iter.hasNext()) {
                return OptionalLong.empty();
            }

            long next = iter.nextLong();

            while (iter.hasNext()) {
                next = iter.nextLong();
            }

            return OptionalLong.of(next);
        } finally {
            close();
        }
    }

    @Override
    public OptionalLong onlyOne() throws TooManyElementsException {
        assertNotClosed();

        try {
            final LongIterator iter = this.iteratorEx();

            final OptionalLong result = iter.hasNext() ? OptionalLong.of(iter.nextLong()) : OptionalLong.empty();

            if (result.isPresent() && iter.hasNext()) {
                throw new TooManyElementsException("There are at least two elements: " + StringUtil.concat(result.get(), ", ", iter.nextLong()));
            }

            return result;
        } finally {
            close();
        }
    }

    @Override
    public  OptionalLong findAny(final Throwables.LongPredicate predicate) throws E {
        assertNotClosed();

        return findFirst(predicate);
    }

    @Override
    public  OptionalLong findFirstOrAny(Throwables.LongPredicate predicateForFirst) throws E {
        assertNotClosed();

        try {
            final LongIteratorEx iter = iteratorEx();
            MutableLong ret = null;
            long next = 0;

            while (iter.hasNext()) {
                next = iter.nextLong();

                if (predicateForFirst.test(next)) {
                    return OptionalLong.of(next);
                } else if (ret == null) {
                    ret = MutableLong.of(next);
                }
            }

            return ret == null ? OptionalLong.empty() : OptionalLong.of(ret.value());
        } finally {
            close();
        }
    }

    @Override
    public  OptionalLong findFirstOrLast(Throwables.LongPredicate predicateForFirst) throws E {
        assertNotClosed();

        try {
            final LongIteratorEx iter = iteratorEx();
            MutableLong ret = null;
            long next = 0;

            while (iter.hasNext()) {
                next = iter.nextLong();

                if (predicateForFirst.test(next)) {
                    return OptionalLong.of(next);
                } else if (ret == null) {
                    ret = MutableLong.of(next);
                } else {
                    ret.setValue(next);
                }
            }

            return ret == null ? OptionalLong.empty() : OptionalLong.of(ret.value());
        } finally {
            close();
        }
    }

    @Override
    public Optional> percentiles() {
        assertNotClosed();

        try {
            final long[] a = sorted().toArray();

            if (a.length == 0) {
                return Optional.empty();
            }

            return Optional.of(N.percentiles(a));
        } finally {
            close();
        }
    }

    @Override
    public Pair>> summarizeAndPercentiles() {
        assertNotClosed();

        try {
            final long[] a = sorted().toArray();

            if (N.isNullOrEmpty(a)) {
                return Pair.of(new LongSummaryStatistics(), Optional.> empty());
            } else {
                return Pair.of(new LongSummaryStatistics(a.length, a[0], a[a.length - 1], sum(a)), Optional.of(N.percentiles(a)));
            }
        } finally {
            close();
        }
    }

    @Override
    public String join(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix) {
        assertNotClosed();

        try {
            final Joiner joiner = Joiner.with(delimiter, prefix, suffix).reuseCachedBuffer();
            final LongIteratorEx iter = this.iteratorEx();

            while (iter.hasNext()) {
                joiner.append(iter.nextLong());
            }

            return joiner.toString();
        } finally {
            close();
        }
    }

    @Override
    public  R collect(Supplier supplier, ObjLongConsumer accumulator) {
        assertNotClosed();

        final BiConsumer combiner = collectingCombiner;

        return collect(supplier, accumulator, combiner);
    }
}