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

io.lacuna.bifurcan.utils.Iterators Maven / Gradle / Ivy

package io.lacuna.bifurcan.utils;

import io.lacuna.bifurcan.LinearList;

import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.LongFunction;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * @author ztellman
 */
public class Iterators {

  private static final Object NONE = new Object();

  public static final Iterator EMPTY = new Iterator() {
    @Override
    public boolean hasNext() {
      return false;
    }

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

  /**
   * A utility class for dynamically appending and prepending iterators to a collection, which itself can be iterated
   * over.
   */
  public static class IteratorStack implements Iterator {

    LinearList> iterators = new LinearList<>();

    public IteratorStack() {
    }

    public IteratorStack(Iterator... its) {
      for (Iterator it : its) {
        iterators.addFirst(it);
      }
    }

    private void primeIterator() {
      while (iterators.size() > 0 && !iterators.first().hasNext()) {
        iterators.removeFirst();
      }
    }

    @Override
    public boolean hasNext() {
      primeIterator();
      return iterators.size() > 0 && iterators.first().hasNext();
    }

    @Override
    public V next() {
      primeIterator();
      if (iterators.size() == 0) {
        throw new NoSuchElementException();
      }
      return iterators.first().next();
    }

    public void addFirst(Iterator it) {
      iterators.addFirst(it);
    }

    public void addLast(Iterator it) {
      iterators.addLast(it);
    }
  }

  public static  boolean equals(Iterator a, Iterator b, BiPredicate equals) {
    while (a.hasNext()) {
      if (!equals.test(a.next(), b.next())) {
        return false;
      }
    }
    return true;
  }

  public static  Iterator singleton(V val) {
    return new Iterator() {
      boolean consumed = false;

      @Override
      public boolean hasNext() {
        return !consumed;
      }

      @Override
      public V next() {
        if (!consumed) {
          consumed = true;
          return val;
        } else {
          throw new NoSuchElementException();
        }
      }
    };
  }

  /**
   * @param it an iterator
   * @param f a predicate
   * @return an iterator which only yields values that satisfy the predicate
   */
  public static  Iterator filter(Iterator it, Predicate f) {
    return new Iterator() {

      private Object next = NONE;
      private boolean done = false;

      private void prime() {
        if (next == NONE && !done) {
          while (it.hasNext()) {
            next = it.next();
            if (f.test((V) next)) {
              return;
            }
          }
          done = true;
        }
      }

      @Override
      public boolean hasNext() {
        prime();
        return !done;
      }

      @Override
      public V next() {
        prime();
        if (next == NONE) {
          throw new NoSuchElementException();
        }

        V val = (V) next;
        next = NONE;
        return val;
      }
    };
  }

  /**
   * @param it an iterator
   * @param f a function which transforms values
   * @return an iterator which yields the transformed values
   */
  public static  Iterator map(Iterator it, Function f) {
    return new Iterator() {
      @Override
      public boolean hasNext() {
        return it.hasNext();
      }

      @Override
      public V next() {
        return f.apply(it.next());
      }
    };
  }

  /**
   * @param it an iterator
   * @param f a function which transforms values into iterators
   * @return an iterator which yields the concatenation of the iterators
   */
  public static  Iterator flatMap(Iterator it, Function> f) {
    return new Iterator() {

      Iterator curr = EMPTY;

      private void prime() {
        while (!curr.hasNext() && it.hasNext()) {
          curr = f.apply(it.next());
        }
      }

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

      @Override
      public V next() {
        prime();
        return curr.next();
      }
    };
  }

  /**
   * @param min an inclusive start of the range
   * @param max an exclusive end of the range
   * @param f a function which transforms a number in the range into a value
   * @return an iterator which yields the values returned by {@code f}
   */
  public static  Iterator range(long min, long max, LongFunction f) {
    return new Iterator() {

      long i = min;

      @Override
      public boolean hasNext() {
        return i < max;
      }

      @Override
      public V next() {
        if (hasNext()) {
          return f.apply(i++);
        } else {
          throw new NoSuchElementException();
        }
      }
    };
  }

  /**
   * Represents a range implicitly starting at 0.
   *
   * @param max an exclusive end of the range.
   * @param f a function which transforms a number in the range into a value.
   * @return an iterator which yields the values returned by {@code f}
   */
  public static  Iterator range(long max, LongFunction f) {
    return range(0, max, f);
  }

  /**
   * @param iterators a list of iterators
   * @return a concatenation of all iterators, in the order provided
   */
  public static  Iterator concat(Iterator... iterators) {
    if (iterators.length == 1) {
      return iterators[0];
    } else {
      IteratorStack stack = new IteratorStack();
      for (Iterator it : iterators) {
        stack.addLast(it);
      }
      return stack;
    }
  }

  public static  Stream toStream(Iterator it) {
    return toStream(it, 0);
  }

  public static  Stream toStream(Iterator it, long estimatedSize) {
    return StreamSupport.stream(Spliterators.spliterator(it, estimatedSize, Spliterator.ORDERED), false);
  }

}