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

com.landawn.abacus.util.TriIterator 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.

The newest version!
/*
 * Copyright (c) 2018, 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;

import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Supplier;

import com.landawn.abacus.util.Fn.Suppliers;
import com.landawn.abacus.util.u.Optional;
import com.landawn.abacus.util.function.IntObjConsumer;
import com.landawn.abacus.util.function.TriConsumer;
import com.landawn.abacus.util.function.TriFunction;
import com.landawn.abacus.util.function.TriPredicate;
import com.landawn.abacus.util.stream.Stream;

/**
 * The TriIterator class is an abstract class that extends ImmutableIterator.
 * It represents an iterator over a triple of values of type A, B, and C.
 * This class provides a blueprint for classes that need to implement a tri-directional iterator.
 *
 * @param  the first type of elements returned by this iterator
 * @param  the second type of elements returned by this iterator
 * @param  the third type of elements returned by this iterator
 *
 * @see com.landawn.abacus.util.Iterators
 * @see com.landawn.abacus.util.Enumerations
 *
 */
@SuppressWarnings({ "java:S6548" })
public abstract class TriIterator extends ImmutableIterator> {

    @SuppressWarnings("rawtypes")
    private static final TriIterator EMPTY = new TriIterator() {
        @Override
        public boolean hasNext() {
            return false;
        }

        @Override
        public Object next() {
            throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
        }

        @Override
        protected void next(final Throwables.TriConsumer action) throws NoSuchElementException {
            throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
        }

        @Override
        public void forEachRemaining(final TriConsumer action) throws IllegalArgumentException {
            N.checkArgNotNull(action);
        }

        @Override
        public void foreachRemaining(final Throwables.TriConsumer action) throws IllegalArgumentException {
            N.checkArgNotNull(action);
        }

        @Override
        public ObjIterator map(final TriFunction mapper) throws IllegalArgumentException {
            N.checkArgNotNull(mapper, cs.mapper);

            return ObjIterator.empty();
        }
    };

    /**
     * Returns an empty TriIterator.
     *
     * @param  the first type of elements returned by this iterator
     * @param  the second type of elements returned by this iterator
     * @param  the third type of elements returned by this iterator
     * @return an empty TriIterator
     */
    public static  TriIterator empty() {
        return EMPTY;
    }

    /**
     * Generates an infinite {@code TriIterator} instance with the provided output Consumer.
     * The output Consumer is responsible for producing the next Triple on each iteration.
     *
     * @param  the first type of elements returned by this iterator
     * @param  the second type of elements returned by this iterator
     * @param  the third type of elements returned by this iterator
     * @param output A Consumer that accepts a Triple and produces the next Triple on each iteration.
     * @return A TriIterator that uses the provided output Consumer to generate its elements.
     * @see  #generate(BooleanSupplier, Consumer)
     */
    public static  TriIterator generate(final Consumer> output) {
        return generate(com.landawn.abacus.util.function.BooleanSupplier.TRUE, output);
    }

    /**
     * Generates a TriIterator instance with the provided hasNext BooleanSupplier and output Consumer.
     * The hasNext BooleanSupplier is used to determine if the iterator has more elements.
     * The output Consumer is responsible for producing the next Triple on each iteration.
     *
     * @param  the first type of elements returned by this iterator
     * @param  the second type of elements returned by this iterator
     * @param  the third type of elements returned by this iterator
     * @param hasNext A BooleanSupplier that returns {@code true} if the iterator has more elements.
     * @param output A Consumer that accepts a Triple and produces the next Triple on each iteration.
     * @return A TriIterator that uses the provided hasNext BooleanSupplier and output Consumer to generate its elements.
     * @throws IllegalArgumentException If hasNext or output is {@code null}.
     */
    public static  TriIterator generate(final BooleanSupplier hasNext, final Consumer> output)
            throws IllegalArgumentException {
        N.checkArgNotNull(hasNext);
        N.checkArgNotNull(output);

        return new TriIterator<>() {
            private final Triple tmp = new Triple<>();

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

            @Override
            public Triple next() throws IllegalArgumentException {
                if (!hasNext()) {
                    throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                }

                output.accept(tmp);

                return Triple.of(tmp.left, tmp.middle, tmp.right);
            }

            @Override
            protected  void next(final Throwables.TriConsumer action)
                    throws NoSuchElementException, E {
                // N.checkArgNotNull(action);

                if (!hasNext()) {
                    throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                }

                output.accept(tmp);

                action.accept(tmp.left, tmp.middle, tmp.right);
            }

            @Override
            public void forEachRemaining(final TriConsumer action) throws IllegalArgumentException {
                N.checkArgNotNull(action);

                while (hasNext.getAsBoolean()) {
                    output.accept(tmp);

                    action.accept(tmp.left, tmp.middle, tmp.right);
                }
            }

            @Override
            public  void foreachRemaining(final Throwables.TriConsumer action)
                    throws IllegalArgumentException, E {
                N.checkArgNotNull(action);

                while (hasNext.getAsBoolean()) {
                    output.accept(tmp);

                    action.accept(tmp.left, tmp.middle, tmp.right);
                }
            }

            @Override
            public  ObjIterator map(final TriFunction mapper) throws IllegalArgumentException {
                N.checkArgNotNull(mapper);

                return new ObjIterator<>() {
                    @Override
                    public boolean hasNext() {
                        return hasNext.getAsBoolean();
                    }

                    @Override
                    public R next() {
                        if (!hasNext()) {
                            throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                        }

                        output.accept(tmp);

                        return mapper.apply(tmp.left, tmp.middle, tmp.right);
                    }
                };
            }
        };
    }

    /**
     * Generates a TriIterator instance with the provided fromIndex, toIndex, and output IntObjConsumer.
     * The fromIndex and toIndex define the size of the returned iterator.
     * The output IntObjConsumer is responsible for producing the next Triple on each iteration.
     *
     * @param  the first type of elements returned by this iterator
     * @param  the second type of elements returned by this iterator
     * @param  the third type of elements returned by this iterator
     * @param fromIndex The starting index of the iterator.
     * @param toIndex The ending index of the iterator.
     * @param output An IntObjConsumer that accepts an integer and a Triple and produces the next Triple on each iteration.
     * @return A TriIterator that uses the provided fromIndex, toIndex, and output IntObjConsumer to generate its elements.
     * @throws IllegalArgumentException If fromIndex is greater than toIndex.
     * @throws IndexOutOfBoundsException If fromIndex or toIndex is out of range.
     */
    public static  TriIterator generate(final int fromIndex, final int toIndex, final IntObjConsumer> output)
            throws IllegalArgumentException, IndexOutOfBoundsException {
        N.checkFromToIndex(fromIndex, toIndex, Integer.MAX_VALUE);
        N.checkArgNotNull(output);

        return new TriIterator<>() {
            private final MutableInt cursor = MutableInt.of(fromIndex);
            private final Triple tmp = new Triple<>();

            @Override
            public boolean hasNext() {
                return cursor.value() < toIndex;
            }

            @Override
            public Triple next() throws IllegalArgumentException {
                if (!hasNext()) {
                    throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                }

                output.accept(cursor.getAndIncrement(), tmp);

                return Triple.of(tmp.left, tmp.middle, tmp.right);
            }

            @Override
            protected  void next(final Throwables.TriConsumer action)
                    throws NoSuchElementException, E {
                // N.checkArgNotNull(action);

                if (!hasNext()) {
                    throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                }

                output.accept(cursor.getAndIncrement(), tmp);

                action.accept(tmp.left, tmp.middle, tmp.right);
            }

            @Override
            public void forEachRemaining(final TriConsumer action) throws IllegalArgumentException {
                N.checkArgNotNull(action);

                while (cursor.value() < toIndex) {
                    output.accept(cursor.getAndIncrement(), tmp);

                    action.accept(tmp.left, tmp.middle, tmp.right);
                }
            }

            @Override
            public  void foreachRemaining(final Throwables.TriConsumer action)
                    throws IllegalArgumentException, E {
                N.checkArgNotNull(action);

                while (cursor.value() < toIndex) {
                    output.accept(cursor.getAndIncrement(), tmp);

                    action.accept(tmp.left, tmp.middle, tmp.right);
                }
            }

            @Override
            public  ObjIterator map(final TriFunction mapper) throws IllegalArgumentException {
                N.checkArgNotNull(mapper);

                return new ObjIterator<>() {
                    @Override
                    public boolean hasNext() {
                        return cursor.value() < toIndex;
                    }

                    @Override
                    public R next() {
                        if (!hasNext()) {
                            throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                        }

                        output.accept(cursor.getAndIncrement(), tmp);

                        return mapper.apply(tmp.left, tmp.middle, tmp.right);
                    }
                };
            }
        };
    }

    /**
     * Zips three arrays into a TriIterator.
     * The resulting TriIterator will iterate over triples of elements from the three arrays.
     * If the arrays have different lengths, the resulting TriIterator will have the length of the shortest array.
     * If any of arrays is {@code null}, returns an empty TriIterator.
     *
     * @param  the type of elements in the first array
     * @param  the type of elements in the second array
     * @param  the type of elements in the third array
     * @param a the first array
     * @param b the second array
     * @param c the third array
     * @return a TriIterator that iterates over the elements of the three arrays in parallel
     */
    public static  TriIterator zip(final A[] a, final B[] b, final C[] c) {
        return zip(Array.asList(a), Array.asList(b), Array.asList(c));
    }

    /**
     * Zips three arrays into a TriIterator with specified default values for missing elements.
     * The resulting TriIterator will iterate over triples of elements from the three arrays.
     * If the arrays have different lengths, the resulting TriIterator will continue with the default values
     * for the shorter array until the longest array is exhausted.
     *
     * @param  the type of elements in the first array
     * @param  the type of elements in the second array
     * @param  the type of elements in the third array
     * @param a the first array
     * @param b the second array
     * @param c the third array
     * @param valueForNoneA the default value for missing elements in the first array
     * @param valueForNoneB the default value for missing elements in the second array
     * @param valueForNoneC the default value for missing elements in the third array
     * @return a TriIterator that iterates over the elements of the three arrays in parallel, using default values for missing elements
     */
    public static  TriIterator zip(final A[] a, final B[] b, final C[] c, final A valueForNoneA, final B valueForNoneB,
            final C valueForNoneC) {
        return zip(Array.asList(a), Array.asList(b), Array.asList(c), valueForNoneA, valueForNoneB, valueForNoneC);
    }

    /**
     * Zips three iterables into a TriIterator.
     * The resulting TriIterator will iterate over triples of elements from the three iterables.
     * If the iterables have different lengths, the resulting TriIterator will have the length of the shortest iterable.
     * If any of iterable is {@code null}, returns an empty TriIterator.
     *
     * @param  the type of elements in the first iterable
     * @param  the type of elements in the second iterable
     * @param  the type of elements in the third iterable
     * @param a the first iterable
     * @param b the second iterable
     * @param c the third iterable
     * @return a TriIterator that iterates over the elements of the three iterables in parallel
     */
    public static  TriIterator zip(final Iterable a, final Iterable b, final Iterable c) {
        return zip(a == null ? null : a.iterator(), b == null ? null : b.iterator(), c == null ? null : c.iterator());
    }

    /**
     * Zips three iterables into a TriIterator with specified default values for missing elements.
     * The resulting TriIterator will iterate over triples of elements from the three iterables.
     * If the iterables have different lengths, the resulting TriIterator will continue with the default values
     * for the shorter iterable until the longest iterable is exhausted.
     *
     * @param  the type of elements in the first iterable
     * @param  the type of elements in the second iterable
     * @param  the type of elements in the third iterable
     * @param a the first iterable
     * @param b the second iterable
     * @param c the third iterable
     * @param valueForNoneA the default value for missing elements in the first iterable
     * @param valueForNoneB the default value for missing elements in the second iterable
     * @param valueForNoneC the default value for missing elements in the third iterable
     * @return a TriIterator that iterates over the elements of the three iterables in parallel, using default values for missing elements
     */
    public static  TriIterator zip(final Iterable a, final Iterable b, final Iterable c, final A valueForNoneA,
            final B valueForNoneB, final C valueForNoneC) {
        return zip(a == null ? null : a.iterator(), b == null ? null : b.iterator(), c == null ? null : c.iterator(), valueForNoneA, valueForNoneB,
                valueForNoneC);
    }

    /**
     * Zips three iterators into a TriIterator.
     * The resulting TriIterator will iterate over triples of elements from the three iterators.
     * If the iterators have different lengths, the resulting TriIterator will have the length of the shortest iterator.
     * If any of iterator is {@code null}, returns an empty TriIterator.
     *
     * @param  the type of elements in the first iterator
     * @param  the type of elements in the second iterator
     * @param  the type of elements in the third iterator
     * @param iterA the first iterator
     * @param iterB the second iterator
     * @param iterC the third iterator
     * @return a TriIterator that iterates over the elements of the three iterators in parallel
     */
    public static  TriIterator zip(final Iterator iterA, final Iterator iterB, final Iterator iterC) {
        if (iterA == null || iterB == null || iterC == null) {
            return empty();
        }

        return new TriIterator<>() {
            @Override
            public boolean hasNext() {
                return iterA.hasNext() && iterB.hasNext() && iterC.hasNext();
            }

            @Override
            public Triple next() throws IllegalArgumentException {
                if (!hasNext()) {
                    throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                }

                return Triple.of(iterA.next(), iterB.next(), iterC.next());
            }

            @Override
            protected  void next(final Throwables.TriConsumer action)
                    throws NoSuchElementException, E {
                // N.checkArgNotNull(action);

                action.accept(iterA.next(), iterB.next(), iterC.next());
            }

            @Override
            public void forEachRemaining(final TriConsumer action) throws IllegalArgumentException {
                N.checkArgNotNull(action);

                while (iterA.hasNext() && iterB.hasNext() && iterC.hasNext()) {
                    action.accept(iterA.next(), iterB.next(), iterC.next());
                }
            }

            @Override
            public  void foreachRemaining(final Throwables.TriConsumer action)
                    throws IllegalArgumentException, E {
                N.checkArgNotNull(action);

                while (iterA.hasNext() && iterB.hasNext() && iterC.hasNext()) {
                    action.accept(iterA.next(), iterB.next(), iterC.next());
                }
            }

            @Override
            public  ObjIterator map(final TriFunction mapper) throws IllegalArgumentException {
                N.checkArgNotNull(mapper);

                return new ObjIterator<>() {
                    @Override
                    public boolean hasNext() {
                        return iterA.hasNext() && iterB.hasNext() && iterC.hasNext();
                    }

                    @Override
                    public R next() {
                        if (!hasNext()) {
                            throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                        }

                        return mapper.apply(iterA.next(), iterB.next(), iterC.next());
                    }
                };
            }
        };
    }

    /**
     * Zips three iterators into a TriIterator with specified default values for missing elements.
     * The resulting TriIterator will iterate over triples of elements from the three iterators.
     * If the iterators have different lengths, the resulting TriIterator will continue with the default values
     * for the shorter iterator until the longest iterator is exhausted.
     *
     * @param  the type of elements in the first iterator
     * @param  the type of elements in the second iterator
     * @param  the type of elements in the third iterator
     * @param iterA the first iterator
     * @param iterB the second iterator
     * @param iterC the third iterator
     * @param valueForNoneA the default value for missing elements in the first iterator
     * @param valueForNoneB the default value for missing elements in the second iterator
     * @param valueForNoneC the default value for missing elements in the third iterator
     * @return a TriIterator that iterates over the elements of the three iterators in parallel, using default values for missing elements
     */
    public static  TriIterator zip(final Iterator iterA, final Iterator iterB, final Iterator iterC, final A valueForNoneA,
            final B valueForNoneB, final C valueForNoneC) {
        final Iterator iter1 = iterA == null ? ObjIterator.empty() : iterA;
        final Iterator iter2 = iterB == null ? ObjIterator.empty() : iterB;
        final Iterator iter3 = iterC == null ? ObjIterator.empty() : iterC;

        return new TriIterator<>() {
            @Override
            public boolean hasNext() {
                return iter1.hasNext() || iter2.hasNext() || iter3.hasNext();
            }

            @Override
            public Triple next() throws IllegalArgumentException {
                if (!hasNext()) {
                    throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                }

                return Triple.of(iter1.hasNext() ? iter1.next() : valueForNoneA, iter2.hasNext() ? iter2.next() : valueForNoneB,
                        iter3.hasNext() ? iter3.next() : valueForNoneC);
            }

            @Override
            protected  void next(final Throwables.TriConsumer action)
                    throws NoSuchElementException, E {
                // N.checkArgNotNull(action);

                if (!hasNext()) {
                    throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                }

                action.accept(iter1.hasNext() ? iter1.next() : valueForNoneA, iter2.hasNext() ? iter2.next() : valueForNoneB,
                        iter3.hasNext() ? iter3.next() : valueForNoneC);
            }

            @Override
            public void forEachRemaining(final TriConsumer action) throws IllegalArgumentException {
                N.checkArgNotNull(action);

                while (iter1.hasNext() || iter2.hasNext() || iter3.hasNext()) {
                    action.accept(iter1.hasNext() ? iter1.next() : valueForNoneA, iter2.hasNext() ? iter2.next() : valueForNoneB,
                            iter3.hasNext() ? iter3.next() : valueForNoneC);
                }
            }

            @Override
            public  void foreachRemaining(final Throwables.TriConsumer action)
                    throws IllegalArgumentException, E {
                N.checkArgNotNull(action);

                while (iter1.hasNext() || iter2.hasNext() || iter3.hasNext()) {
                    action.accept(iter1.hasNext() ? iter1.next() : valueForNoneA, iter2.hasNext() ? iter2.next() : valueForNoneB,
                            iter3.hasNext() ? iter3.next() : valueForNoneC);
                }
            }

            @Override
            public  ObjIterator map(final TriFunction mapper) throws IllegalArgumentException {
                N.checkArgNotNull(mapper);

                return new ObjIterator<>() {
                    @Override
                    public boolean hasNext() {
                        return iter1.hasNext() || iter2.hasNext() || iter3.hasNext();
                    }

                    @Override
                    public R next() {
                        if (!hasNext()) {
                            throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                        }

                        return mapper.apply(iter1.hasNext() ? iter1.next() : valueForNoneA, iter2.hasNext() ? iter2.next() : valueForNoneB,
                                iter3.hasNext() ? iter3.next() : valueForNoneC);
                    }
                };
            }
        };
    }

    /**
     * Unzips an iterable of elements into a TriIterator.
     * The resulting TriIterator will iterate over triples of elements produced by the unzip function.
     * If the iterable is {@code null}, an empty TriIterator is returned.
     *
     * @param  the type of elements in the input iterable
     * @param  the type of elements in the first component of the triple
     * @param  the type of elements in the second component of the triple
     * @param  the type of elements in the third component of the triple
     * @param iter the input iterable
     * @param unzipFunc a BiConsumer that accepts an element of type T and a {@code Triple} and populates the triple with the unzipped values
     * @return a TriIterator that iterates over the unzipped elements
     */
    public static  TriIterator unzip(final Iterable iter, final BiConsumer> unzipFunc) {
        if (iter == null) {
            return TriIterator.empty();
        }

        return unzip(iter.iterator(), unzipFunc);
    }

    /**
     * Unzips an iterator of elements into a TriIterator.
     * The resulting TriIterator will iterate over triples of elements produced by the unzip function.
     * If the iterator is {@code null}, an empty TriIterator is returned.
     *
     * @param  the type of elements in the input iterator
     * @param  the type of elements in the first component of the triple
     * @param  the type of elements in the second component of the triple
     * @param  the type of elements in the third component of the triple
     * @param iter the input iterator
     * @param unzipFunc a BiConsumer that accepts an element of type T and a Triple and populates the triple with the unzipped values
     * @return a TriIterator that iterates over the unzipped elements
     */
    public static  TriIterator unzip(final Iterator iter, final BiConsumer> unzipFunc) {
        if (iter == null) {
            return TriIterator.empty();
        }

        final BooleanSupplier hasNext = iter::hasNext;

        final Consumer> output = out -> unzipFunc.accept(iter.next(), out);

        return TriIterator.generate(hasNext, output);
    }

    /**
     *
     * @param 
     * @param action
     * @throws NoSuchElementException
     * @throws E
     */
    protected abstract  void next(final Throwables.TriConsumer action)
            throws NoSuchElementException, E;

    /**
     * Performs the given action for each remaining element in the iterator until all elements have been processed or the action throws an exception.
     *
     * @param action the action to be performed for each element
     * @deprecated use {@code forEachRemaining(TriConsumer)} to avoid creating the unnecessary {@code Triple} Objects.
     * @see #forEachRemaining(TriConsumer)
     */
    @Override
    @Deprecated
    public void forEachRemaining(final Consumer> action) {
        super.forEachRemaining(action);
    }

    /**
     * Performs the given action for each remaining element in the iterator until all elements
     * have been processed or the action throws an exception.
     *
     * @param action the action to be performed for each element
     */
    public abstract void forEachRemaining(final TriConsumer action);

    /**
     * Performs the given action for each remaining element in the iterator until all elements
     * have been processed or the action throws an exception.
     *
     * @param  the type of exception that the action may throw
     * @param action the action to be performed for each element
     * @throws E if the action throws an exception
     */
    public abstract  void foreachRemaining(final Throwables.TriConsumer action) throws E; // NOSONAR

    @SuppressWarnings("rawtypes")
    private static final Throwables.TriConsumer DO_NOTHING = (a, b, c) -> {
        // do nothing;
    };

    /**
     * Returns a new TriIterator with n elements skipped from the beginning of this TriIterator.
     *
     * @param n the number of elements to skip
     * @return A new TriIterator that skips the first n elements.
     * @throws IllegalArgumentException If n is negative.
     */
    public TriIterator skip(final long n) throws IllegalArgumentException {
        N.checkArgNotNegative(n, cs.n);

        if (n <= 0) {
            return this;
        }

        final TriIterator iter = this;

        return new TriIterator<>() {
            private boolean skipped = false;

            @Override
            public boolean hasNext() {
                if (!skipped) {
                    skip();
                }

                return iter.hasNext();
            }

            @Override
            public Triple next() {
                if (!hasNext()) {
                    throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                }

                return iter.next();
            }

            @Override
            protected  void next(final Throwables.TriConsumer action)
                    throws NoSuchElementException, E {
                if (!skipped) {
                    skip();
                }

                iter.next(action);
            }

            @Override
            public void forEachRemaining(final TriConsumer action) {
                if (!skipped) {
                    skip();
                }

                iter.forEachRemaining(action);
            }

            @Override
            public  void foreachRemaining(final Throwables.TriConsumer action) throws E {
                if (!skipped) {
                    skip();
                }

                iter.foreachRemaining(action);
            }

            @Override
            public  ObjIterator map(final TriFunction mapper) {
                N.checkArgNotNull(mapper, cs.mapper);

                if (!skipped) {
                    skip();
                }

                return iter.map(mapper);
            }

            private void skip() {
                long idx = 0;

                while (idx++ < n && iter.hasNext()) {
                    iter.next(DO_NOTHING);
                }

                skipped = true;
            }
        };
    }

    /**
     * Returns a new TriIterator with a limited number of elements.
     * The resulting TriIterator will contain at most the specified number of elements.
     *
     * @param count the maximum number of elements to include in the resulting TriIterator
     * @return a new TriIterator that contains at most the specified number of elements
     * @throws IllegalArgumentException If count is negative.
     */
    public TriIterator limit(final long count) throws IllegalArgumentException {
        N.checkArgNotNegative(count, cs.count);

        if (count == 0) {
            return TriIterator.empty();
        }

        final TriIterator iter = this;

        return new TriIterator<>() {
            private long cnt = count;

            @Override
            public boolean hasNext() {
                return cnt > 0 && iter.hasNext();
            }

            @Override
            public Triple next() {
                if (!hasNext()) {
                    throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                }

                cnt--;
                return iter.next();
            }

            @Override
            protected  void next(final Throwables.TriConsumer action)
                    throws NoSuchElementException, E {
                if (!hasNext()) {
                    throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                }

                cnt--;
                iter.next(action);
            }

            @Override
            public void forEachRemaining(final TriConsumer action) {
                while (hasNext()) {
                    cnt--;
                    iter.next(action);
                }
            }

            @Override
            public  void foreachRemaining(final Throwables.TriConsumer action) throws E {
                while (hasNext()) {
                    cnt--;
                    iter.next(action);
                }
            }

            @Override
            public  ObjIterator map(final TriFunction mapper) {
                N.checkArgNotNull(mapper, cs.mapper);

                if (cnt > 0) {
                    return iter. map(mapper).limit(cnt);
                } else {
                    return ObjIterator.empty();
                }
            }
        };
    }

    /**
     * Returns a new TriIterator that includes only the elements that satisfy the provided predicate.
     *
     * @param predicate the predicate to apply to each pair of elements
     * @return a new TriIterator containing only the elements that match the predicate
     */
    public TriIterator filter(final TriPredicate predicate) {
        N.checkArgNotNull(predicate, cs.Predicate);

        final TriIterator iter = this;

        return new TriIterator<>() {
            private final Triple next = new Triple<>();
            private final Throwables.TriConsumer setNext = next::set;

            private boolean hasNext = false;

            @Override
            public boolean hasNext() {
                if (!hasNext) {
                    while (iter.hasNext()) {
                        iter.next(setNext);

                        if (predicate.test(next.left, next.middle, next.right)) {
                            hasNext = true;
                            break;
                        }
                    }
                }

                return hasNext;
            }

            @Override
            public Triple next() {
                if (!hasNext && !hasNext()) {
                    throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                }

                hasNext = false;

                return next.copy();
            }

            @Override
            protected  void next(final Throwables.TriConsumer action)
                    throws NoSuchElementException, E {
                if (!hasNext && !hasNext()) {
                    throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                }

                hasNext = false;

                action.accept(next.left, next.middle, next.right);
            }

            @Override
            public void forEachRemaining(final TriConsumer action) {
                while (hasNext()) {
                    hasNext = false;

                    action.accept(next.left, next.middle, next.right);
                }
            }

            @Override
            public  void foreachRemaining(final Throwables.TriConsumer action) throws E {
                while (hasNext()) {
                    hasNext = false;

                    action.accept(next.left, next.middle, next.right);
                }
            }

            @Override
            public  ObjIterator map(final TriFunction mapper) {
                N.checkArgNotNull(mapper, cs.mapper);

                return new ObjIterator<>() {
                    @Override
                    public boolean hasNext() {
                        if (!hasNext) {
                            while (iter.hasNext()) {
                                iter.next(setNext);

                                if (predicate.test(next.left, next.middle, next.right)) {
                                    hasNext = true;
                                    break;
                                }
                            }
                        }

                        return hasNext;
                    }

                    @Override
                    public R next() {
                        if (!hasNext && !hasNext()) {
                            throw new NoSuchElementException(InternalUtil.ERROR_MSG_FOR_NO_SUCH_EX);
                        }

                        hasNext = false;

                        return mapper.apply(next.left, next.middle, next.right);
                    }
                };
            }
        };
    }

    /**
     * Transforms the elements of this TriIterator using the given mapper function.
     *
     * @param  the type of elements in the resulting ObjIterator
     * @param mapper the function to apply to each triple of elements
     * @return an ObjIterator containing the elements produced by the mapper function
     */
    public abstract  ObjIterator map(final TriFunction mapper);

    /**
     * Returns an Optional containing the first triple of elements in the iterator.
     * If the iterator is empty, returns an empty Optional.
     *
     * @return an Optional containing the first triple of elements, or an empty Optional if the iterator is empty
     */
    public Optional> first() {
        if (hasNext()) {
            return Optional.of(next());
        } else {
            return Optional.empty();
        }
    }

    /**
     * Returns an Optional containing the last triple of elements in the iterator.
     * If the iterator is empty, returns an empty Optional.
     *
     * @return an Optional containing the last triple of elements, or an empty Optional if the iterator is empty
     */
    public Optional> last() {
        if (hasNext()) {
            final Triple next = new Triple<>();
            final Throwables.TriConsumer setNext = next::set;

            foreachRemaining(setNext);

            return Optional.of(next);
        } else {
            return Optional.empty();
        }
    }

    /**
     * Returns a Stream of elements produced by applying the given mapper function to each triple of elements in this TriIterator.
     *
     * @param  the type of elements in the resulting Stream
     * @param mapper the function to apply to each triple of elements
     * @return a Stream containing the elements produced by the mapper function
     */
    public  Stream stream(final TriFunction mapper) {
        N.checkArgNotNull(mapper, cs.mapper);

        return Stream.of(map(mapper));
    }

    /**
     * Converts the elements in this TriIterator to an array of Triple objects.
     *
     * @return An array containing the remaining triples of elements in this TriIterator.
     */
    public Triple[] toArray() {
        return toArray(new Triple[0]);
    }

    /**
     * Converts the elements in this TriIterator to an array of the specified type.
     *
     * @param  the type of the array elements. It should be super type of Triple.
     * @param a the array into which the elements of this TriIterator are to be stored, if it is big enough;
     *          otherwise, a new array of the same runtime type is allocated for this purpose.
     * @return an array containing the elements of this TriIterator
     * @deprecated This method is deprecated. Use {@link #toArray()} or {@link #toList()} instead.
     */
    @Deprecated
    public  T[] toArray(final T[] a) {
        return toList().toArray(a);
    }

    /**
     * Converts the elements in this TriIterator to a List of Triple objects.
     *
     * @return a List containing all triples of elements in this TriIterator
     */
    public List> toList() {
        return toCollection(Suppliers.ofList());
    }

    /**
     * Converts the elements in this TriIterator to three separate lists of type A, B, and C.
     * The resulting Triple contains three lists, each containing the elements of the corresponding type.
     *
     * @param supplier a Supplier that provides new instances of List for storing the elements
     * @return a Triple containing three lists of elements of type A, B, and C
     */
    public Triple, List, List> toMultiList(@SuppressWarnings("rawtypes") final Supplier supplier) {
        final List listA = supplier.get();
        final List listB = supplier.get();
        final List listC = supplier.get();

        this.foreachRemaining((a, b, c) -> {
            listA.add(a);
            listB.add(b);
            listC.add(c);
        });

        return Triple.of(listA, listB, listC);
    }

    /**
     * Converts the elements in this TriIterator to three separate sets of type A, B, and C.
     * The resulting Triple contains three sets, each containing the elements of the corresponding type.
     *
     * @param supplier a Supplier that provides new instances of Set for storing the elements
     * @return a Triple containing three sets of elements of type A, B, and C
     */
    public Triple, Set, Set> toMultiSet(@SuppressWarnings("rawtypes") final Supplier supplier) {
        final Set listA = supplier.get();
        final Set listB = supplier.get();
        final Set listC = supplier.get();

        this.foreachRemaining((a, b, c) -> {
            listA.add(a);
            listB.add(b);
            listC.add(c);
        });

        return Triple.of(listA, listB, listC);
    }
}