content: /*
   * Copyright 2015, 2024 StreamEx contributors
   *
   * 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 one.util.streamex;
  
  import java.util.List;
  import java.util.Map.Entry;
  import java.util.Spliterator;
  import java.util.function.*;
  
  import static one.util.streamex.Internals.CloneableSpliterator;
  import static one.util.streamex.Internals.ObjIntBox;
  
  /**
   * @author Tagir Valeev
   */
  /* package */abstract class RangeBasedSpliterator<T, S extends RangeBasedSpliterator<T, ?>> extends CloneableSpliterator<T, S> {
      int cur;
      int limit;
  
      public RangeBasedSpliterator(int fromInclusive, int toExclusive) {
          this.cur = fromInclusive;
          this.limit = toExclusive;
      }
  
      @Override
      public long estimateSize() {
          return limit - cur;
      }
  
      @Override
      public int characteristics() {
          return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
      }
  
      @Override
      public S trySplit() {
          int size = limit - cur;
          if (size >= 2) {
              S clone = doClone();
              clone.limit = this.cur = this.cur + size / 2;
              return clone;
          }
          return null;
      }
  
      static final class AsEntry<T> extends RangeBasedSpliterator<Entry<Integer, T>, AsEntry<T>> {
          private final List<T> list;
  
          public AsEntry(List<T> list) {
              super(0, list.size());
              this.list = list;
          }
  
          @Override
          public boolean tryAdvance(Consumer<? super Entry<Integer, T>> action) {
              if (cur < limit) {
                  action.accept(new ObjIntBox<>(list.get(cur), cur));
                  cur++;
                  return true;
              }
              return false;
          }
  
          @Override
          public void forEachRemaining(Consumer<? super Entry<Integer, T>> action) {
              int l = limit, c = cur;
              List<T> list = this.list;
              while (c < l) {
                  action.accept(new ObjIntBox<>(list.get(c), c));
                  c++;
              }
              cur = limit;
          }
      }
  
      static final class OfSubLists<T> extends RangeBasedSpliterator<List<T>, OfSubLists<T>> {
          private final List<T> source;
          private final int length;
          private final int shift;
          private final int listSize;
  
          public OfSubLists(List<T> source, int length, int shift) {
              super(0, Math.max(0, source.size() - Math.max(length - shift, 0) - 1) / shift + 1);
              this.source = source;
              this.listSize = source.size();
              this.shift = shift;
              this.length = length;
          }
  
          @Override
          public boolean tryAdvance(Consumer<? super List<T>> action) {
              if (cur < limit) {
                  int start = cur * shift;
                  int stop = listSize - length > start ? start + length : listSize;
                  action.accept(source.subList(start, stop));
                  cur++;
                  return true;
              }
              return false;
          }
  
          @Override
          public void forEachRemaining(Consumer<? super List<T>> action) {
              int l = limit, c = cur, ll = length, sf = shift, ls = listSize;
              int start = cur * sf;
              while (c < l) {
                  int stop = ls - ll > start ? start + ll : ls;
                  action.accept(source.subList(start, stop));
                  start += sf;
                  c++;
              }
              cur = limit;
          }
      }
  
      static final class ZipRef<U, V, T> extends RangeBasedSpliterator<T, ZipRef<U, V, T>> {
          private final List<U> l1;
          private final List<V> l2;
          private final BiFunction<? super U, ? super V, ? extends T> mapper;
  
          public ZipRef(int fromInclusive, int toExclusive, BiFunction<? super U, ? super V, ? extends T> mapper,
                  List<U> l1, List<V> l2) {
              super(fromInclusive, toExclusive);
              this.l1 = l1;
              this.l2 = l2;
              this.mapper = mapper;
          }
  
          @Override
          public boolean tryAdvance(Consumer<? super T> action) {
              if (cur < limit) {
                  action.accept(mapper.apply(l1.get(cur), l2.get(cur)));
                  cur++;
                  return true;
              }
              return false;
          }
  
          @Override
          public void forEachRemaining(Consumer<? super T> action) {
              int l = limit, c = cur;
              while (c < l) {
                  action.accept(mapper.apply(l1.get(c), l2.get(c)));
                  c++;
              }
              cur = limit;
          }
      }
  
      static final class ZipInt extends RangeBasedSpliterator<Integer, ZipInt> implements Spliterator.OfInt {
          private final IntBinaryOperator mapper;
          private final int[] arr1, arr2;
  
          public ZipInt(int fromInclusive, int toExclusive, IntBinaryOperator mapper, int[] arr1, int[] arr2) {
              super(fromInclusive, toExclusive);
              this.mapper = mapper;
              this.arr1 = arr1;
              this.arr2 = arr2;
          }
  
          @Override
          public boolean tryAdvance(IntConsumer action) {
              if (cur < limit) {
                  action.accept(mapper.applyAsInt(arr1[cur], arr2[cur]));
                  cur++;
                  return true;
              }
              return false;
          }
  
          @Override
          public void forEachRemaining(IntConsumer action) {
              int l = limit, c = cur;
              while (c < l) {
                  action.accept(mapper.applyAsInt(arr1[c], arr2[c]));
                  c++;
              }
              cur = limit;
          }
      }
  
      static final class OfByte extends RangeBasedSpliterator<Integer, OfByte> implements Spliterator.OfInt {
          private final byte[] array;
  
          public OfByte(int fromInclusive, int toExclusive, byte[] array) {
              super(fromInclusive, toExclusive);
              this.array = array;
          }
  
          @Override
          public boolean tryAdvance(IntConsumer action) {
              if (cur < limit) {
                  action.accept(array[cur]);
                  cur++;
                  return true;
              }
              return false;
          }
  
          @Override
          public void forEachRemaining(IntConsumer action) {
              int l = limit, c = cur;
              while (c < l) {
                  action.accept(array[c++]);
              }
              cur = limit;
          }
      }
  
      static final class OfChar extends RangeBasedSpliterator<Integer, OfChar> implements Spliterator.OfInt {
          private final char[] array;
  
          public OfChar(int fromInclusive, int toExclusive, char[] array) {
              super(fromInclusive, toExclusive);
              this.array = array;
          }
  
          @Override
          public boolean tryAdvance(IntConsumer action) {
              if (cur < limit) {
                  action.accept(array[cur]);
                  cur++;
                  return true;
              }
              return false;
          }
  
          @Override
          public void forEachRemaining(IntConsumer action) {
              int l = limit, c = cur;
              while (c < l) {
                  action.accept(array[c++]);
              }
              cur = limit;
          }
      }
  
      static final class OfShort extends RangeBasedSpliterator<Integer, OfShort> implements Spliterator.OfInt {
          private final short[] array;
  
          public OfShort(int fromInclusive, int toExclusive, short[] array) {
              super(fromInclusive, toExclusive);
              this.array = array;
          }
  
          @Override
          public boolean tryAdvance(IntConsumer action) {
              if (cur < limit) {
                  action.accept(array[cur]);
                  cur++;
                  return true;
              }
              return false;
          }
  
          @Override
          public void forEachRemaining(IntConsumer action) {
              int l = limit, c = cur;
              while (c < l) {
                  action.accept(array[c++]);
              }
              cur = limit;
          }
      }
  
      static final class ZipLong extends RangeBasedSpliterator<Long, ZipLong> implements Spliterator.OfLong {
          private final LongBinaryOperator mapper;
          private final long[] arr1, arr2;
  
          public ZipLong(int fromInclusive, int toExclusive, LongBinaryOperator mapper, long[] arr1, long[] arr2) {
              super(fromInclusive, toExclusive);
              this.mapper = mapper;
              this.arr1 = arr1;
              this.arr2 = arr2;
          }
  
          @Override
          public boolean tryAdvance(LongConsumer action) {
              if (cur < limit) {
                  action.accept(mapper.applyAsLong(arr1[cur], arr2[cur]));
                  cur++;
                  return true;
              }
              return false;
          }
  
          @Override
          public void forEachRemaining(LongConsumer action) {
              int l = limit, c = cur;
              while (c < l) {
                  action.accept(mapper.applyAsLong(arr1[c], arr2[c]));
                  c++;
              }
              cur = limit;
          }
      }
  
      static final class OfFloat extends RangeBasedSpliterator<Double, OfFloat> implements Spliterator.OfDouble {
          private final float[] array;
  
          public OfFloat(int fromInclusive, int toExclusive, float[] array) {
              super(fromInclusive, toExclusive);
              this.array = array;
          }
  
          @Override
          public boolean tryAdvance(DoubleConsumer action) {
              if (cur < limit) {
                  action.accept(array[cur]);
                  cur++;
                  return true;
              }
              return false;
          }
  
          @Override
          public void forEachRemaining(DoubleConsumer action) {
              int l = limit, c = cur;
              while (c < l) {
                  action.accept(array[c++]);
              }
              cur = limit;
          }
      }
  
      static final class ZipDouble extends RangeBasedSpliterator<Double, ZipDouble> implements Spliterator.OfDouble {
          private final DoubleBinaryOperator mapper;
          private final double[] arr1, arr2;
  
          public ZipDouble(int fromInclusive, int toExclusive, DoubleBinaryOperator mapper, double[] arr1, double[] arr2) {
              super(fromInclusive, toExclusive);
              this.mapper = mapper;
              this.arr1 = arr1;
              this.arr2 = arr2;
          }
  
          @Override
          public boolean tryAdvance(DoubleConsumer action) {
              if (cur < limit) {
                  action.accept(mapper.applyAsDouble(arr1[cur], arr2[cur]));
                  cur++;
                  return true;
              }
              return false;
          }
  
          @Override
          public void forEachRemaining(DoubleConsumer action) {
              int l = limit, c = cur;
              while (c < l) {
                  action.accept(mapper.applyAsDouble(arr1[c], arr2[c]));
                  c++;
              }
              cur = limit;
          }
      }
  }
  