Iterables.java

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

package stonehorse.candy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;


import static java.util.Collections.emptyIterator;
import static java.util.Objects.requireNonNull;

/**
 * Functions of iterables creating lazy iterators. As they are lazy they can be composed without memory exhaustion.
 * It's all functions which you easily create new ones of to extend the functionality.
 */
public class Iterables{
    /**
     * Tail call return value
     */
    public static class Continuation<V> {
        public final Supplier<Continuation<V>> fun;

        public final V value;
        public final boolean hasValue;

        private Continuation(Supplier<Continuation< V>> fun, V value, boolean hasValue) {
            this.fun = fun;
            this.value = value;
            this.hasValue = hasValue;
        }
        /**
         * return recur to recur withLast f and a
         */
        public static < V> Continuation<V> recur(Supplier<Continuation<V>> f) {
            return new Continuation<>(f, null, false);
        }

        private static <V> Continuation<V> nil() {
            return new Continuation<>(null, null, false);
        }
        /** return done to when last value is found */
        public static <V> Continuation<V> done(V v) {
            return new Continuation<>(null, v, true);
        }
        /**
         * Return stop when done without a value. Null if on trampoline
         */
        public static <V> Continuation< V> stop() {
            return new Continuation<>(null, null, false);
        }
        /**
         * return seq to yield lazy value v, and continue withLast f a
         * f as null is same as done or stop, depending on v
         * Will behave like recur on a trampoline
         */
        public static < V> Continuation< V> seq(Supplier<Continuation<V>> f, V v) {
            return new Continuation<>(f, v, true);
        }

    }

    /**
     * Will recur on fun until done(value)
     */
    public static <V> V trampoline(Supplier<Continuation<V>> fun) {
        boolean value = false;
        Continuation< V> ret = Continuation.nil();
        while (fun != null && !value) {
            ret = fun.get();

            fun = ret.fun;
            value = ret.hasValue;
        }
        return ret.value;
    }

    /**
     * Emits a lazy Iterable recuring until stop, done, null or lack of f
     */
    public static <V> Iterable<V> lazy(final Supplier<Continuation< V>> fun) {
        return ()-> new LazyIterator<>(fun);
    }

    private static class LazyIterator< V> implements Iterator<V> {

        private Supplier<Continuation<V>> fun;
        private V next;
        private boolean hasNext = false;

        public LazyIterator(Supplier<Continuation<V>> fun) {
            this.fun = fun;
        }

        @Override public boolean hasNext() {
            if (hasNext)
                return true;
            if (null == fun)
                return false;
            while (true) {
                Continuation<V> ret = fun.get();
                if(null==ret)
                    return false;
                fun = ret.fun;
                next = ret.value;
                hasNext = ret.hasValue;
                if (hasNext)
                    return true;
                if (null == fun)
                    return false;
            }
        }

        @Override
        public V next() {
            if (hasNext())
                try {
                    hasNext = false;
                    return next;
                } finally {
                    next = null;
                }
            throw new NoSuchElementException();
        }
    }

    private static <T> List<T> arrayList(
            T t0,
            T t1){
        List<T> l = new ArrayList<>(2);
        l.add(t0);
        l.add(t1);
        return l;
    }

    private Iterables(){}

    private static < V> Continuation<V> recur(Supplier<Continuation<V>> f) {
        return Continuation.recur(f);
    }

    private static <V> Continuation<V> done(V v) {
        return Continuation.done(v);
    }

    private static <V> Continuation< V> stop() {
        return Continuation.stop();
    }

    private static < V> Continuation< V> seq(Supplier<Continuation<V>> f, V v) {
        return Continuation.seq(f,v);
    }
    /**
     * A new list with elements found in the next iterator of iterable
     */
    public static <A> List<A> toList(Iterable<A> iterable) {
        requireNonNulls(iterable);
        List<A> list = new ArrayList<>();
        for (A e : iterable)
            list.add(e);
        return list;
    }

    /**
     * Combines elements of the first iterator into a single result by repeated application of a combining operation f where the initial result is accumulator
     */
    public static <A, V>
    A fold(BiFunction<? super A, ? super V, ? extends A> f,
           A accumulator,
           Iterable<? extends V> elements) {
        requireNonNulls(f, elements);
        // Accumulate by applying each with the accumulator to the BiFunction
        for (V v : elements)
            accumulator = f.apply(accumulator, v);
        return accumulator;
    }


    /**
     * Iterable of lazy iterators filtering another Iterable using a predicate
     * <p>Example:
     * <pre>{@code
     * toList(filter( e -> 0==e%2, Arrays.asList(1, 2, 3, 4))) => [2, 4]
     * }</pre>
     */
    public static <T>
    Iterable<T> filter(final Predicate<? super T> predicate, // Predicate rather than transformation
                       Iterable<T> source) {
        requireNonNulls(predicate, source);
        return () -> {
            Iterator<T> sourceIterator = source.iterator();
            return sourceIterator.hasNext()
                    ? lazy(filterI(predicate, sourceIterator)).iterator()
                    : emptyIterator();
        };
    }

    private static <T>
    Supplier<Continuation<T>> filterI(Predicate<? super T> predicate,
                                      Iterator<T> elements) {
        return () -> {
            if (!elements.hasNext())
                return stop();
            T next = elements.next();
            return predicate.test(next)
                    ? Continuation.seq(filterI(predicate, elements), next)
                    // Recur rather than seq when the element is ommitted
                    : Continuation.recur(filterI(predicate, elements));
        };
    }
    private static
    void requireNonNulls(Object... refs){
        for(Object o : refs)
            requireNonNull(o);
    }
    /**
     * Iterable of lazy iterators applying a function to all element induced by a Iterable
     * <p>Example:
     * <pre>{@code
     * toList(map( e -> e+1, Arrays.asList(1, 2, 3, 4))) => [2, 3, 4, 5]
     * }</pre>
     */
    public static <A, V> Iterable<V> map(final Function<? super A, ? extends V> f,
                                         final Iterable<A> data) {
        requireNonNulls(data, f);
        return () -> {
            Supplier<Continuation<V>> supplier = () -> mapIterate(f, data.iterator());
            return lazy(supplier).iterator();
        };
    }

    private static <A, V>
    Continuation<V> mapIterate(final Function<? super A,? extends V> f,
                               final Iterator<A> elements) {
        return !elements.hasNext()
                ? stop()
                : seq(()->mapIterate(f, elements),
                f.apply(elements.next()));
    }

    /**
     * The first value of an Iterable
     * <p>Example:
     * <pre>{@code
     * first(Arrays.asList(1, 2, 3, 4)) => 1
     * }</pre>
     */
    public static <T>
    T first(final Iterable<T> iterable) {
        Iterator<T> iterator = iterable.iterator();
        return iterator.hasNext()
               ? iterator.next()
               : null;
    }

    /**
     * All values except first
     * <p>Example:
     * <pre>{@code
     * rest(Arrays.asList(1, 2, 3, 4)) => [2, 3, 4]
     * }</pre> */
    public static <T>
    Iterable<T> rest(Iterable<T> iterable) {
        requireNonNull(iterable);
        return ()->{
            Iterator<T> i = iterable.iterator();
            if(!i.hasNext())
                return emptyIterator();
            i.next();
            return i.hasNext() ? i : emptyIterator();
        };
    }
    /**
     * All values except first, or empty
     */
    public static <T>
    Iterable<T> next(Iterable<T> iterable) {
        requireNonNull(iterable);
        Iterator<?> it = iterable.iterator();
        if(!it.hasNext())
            return null;
        it.next();
        if(!it.hasNext())
            return null;
        return ()->{
            Iterator<T> i = iterable.iterator();
            if(!i.hasNext())
                return emptyIterator();
            i.next();
            if(i.hasNext())
                return i;
            return emptyIterator();
        };
    }

    /**
     * prepend value
     * <p>Example:
     * <pre>{@code
     * with(0,Arrays.asList(1, 2, 3, 4)) => [0, 1, 2, 3, 4]
     * }</pre> *
     */
    public static <T>
    Iterable<T> with(T t,
                     Iterable<? extends T> iterable) {
        requireNonNull(iterable);
        return () -> lazy(withI(t, iterable.iterator())).iterator();
    }

    private static <T>
    Supplier<Continuation<T>> withI(T a, Iterator<? extends T> i) {
        return () -> i.hasNext()
                     ? seq(withI(i.next(), i), a)
                     : done(a);
    }

    /** Append value    * <p>Example:
     * <pre>{@code
     * withLats(,Arrays.asList(1, 2, 3, 4), 5) => [1, 2, 3, 4, 5]
     * }</pre> *
     */
    public static <T>
    Iterable<T> withLast(Iterable<T> iterable, T b){
        requireNonNull(iterable);
        return ()->  lazy(withLastI( iterable.iterator(), b)).iterator();
    }

    private static <T> Supplier<Continuation<T>> withLastI(Iterator<T> a, T b){
        return ()-> a.hasNext()
                    ? seq(withLastI(a,b), a.next())
                    : done(b);
    }

    /**
     * Accumulate into value, with explicit accumulator
     * <p>Example:
     * <pre>{@code
     * reduce((a,v)->a+v, Arrays.asList(1,2,3,4)) => 10
     * }</pre>
     */
    public static <T>
    T reduce(BiFunction<? super T, ? super T, ? extends T> f,
             Iterable<? extends T> elements) {
        return fold(f, first(elements), rest(elements));
    }

    /**
     * Consecutive accumulations with explicit accumulator
     * <p>Example:
     * <pre>{@code
     * reductions((a,v)->a+v, 0, Arrays.asList(1,2,3,4)) => [0, 1, 3, 6, 10]
     * }</pre> */
    public static <A, V>
    Iterable<V> reductions(final BiFunction<? super V, ? super A, ? extends V> f,
                           final V accumulator,
                           final Iterable<? extends A> elements) {
        requireNonNulls(elements, f);
        return () -> with(accumulator,
                          lazy(reductionsI(f,
                                           accumulator,
                                           elements.iterator()))).iterator();
    }

    private static <V, A>
    Supplier<Continuation<V>> reductionsI(BiFunction<? super V, ? super A, ? extends V> f,
                                          V a,
                                          final Iterator<? extends A> elements) {
        return () -> {
            if (!elements.hasNext())
                return stop();
            V acc = f.apply(a, elements.next());
            return elements.hasNext()
                   ? seq(reductionsI(f, acc, elements), acc)
                   : done(acc);
        };
    }
    /** bind of list monad
     * Iterable of lazy iterators which concatenate the result of applying map to every element in iterators of data.
     * <p>Example:
     * <pre>{@code
     *  flatMap(v -> Arrays.asList(v, v), Arrays.asList(1, 2)) => [1, 1, 2, 2]
     * }</pre>
     * */
    public static <V, A>
    Iterable<A> flatMap(Function<? super V, ? extends Iterable<? extends A>> f,
                        Iterable<? extends V> data) {
        requireNonNulls(data, f);
        return lazy(flatMapI(f, null, data.iterator()));
    }

    private static <V, A> Supplier<Continuation<A>> flatMapI(Function<? super V, ? extends Iterable<? extends A>> f,
                                                             Iterator<? extends A> current,
                                                             Iterator<? extends V> data) {
        return () -> {
            if (current != null && current.hasNext()) {
                return seq(flatMapI(f, current, data), current.next());
            }

            while (data.hasNext()) {
                Iterable<? extends A> nextChunk = f.apply(data.next());
                if (nextChunk != null) {
                    Iterator<? extends A> nextIter = nextChunk.iterator();
                    if (nextIter.hasNext()) {
                        return recur(flatMapI(f, nextIter, data));
                    }
                }
            }
            return stop();
        };
    }

    /**
     * Iterable of lazy infinite iterators iterating over function with initial value. That is x, f(x), f(f(x)) and so on
     * <p>Example:
     * <pre>{@code
     * iterate (x -> x + 1, 0) -> [0,1,2,3,4.....
     * }</pre>*/
    public static <T>
    Iterable<T> iterate(Function<? super T, ? extends T> f,
                        T x) {
        requireNonNull(f);
        return () -> with(x, lazy(iterateI(f, x))).iterator();
    }

    private static <T>
    Supplier<Continuation<T>> iterateI(Function<? super T, ? extends T> f,
                                       T x) {
        return () -> {
            T n = f.apply(x);
            return seq(iterateI(f,n), n);
        };
    }

    /**
     * Iterable of lazy iterators having num first values of iterable
     * <p>Example:
     * <pre>{@code
     * take(2, Arrays.asList(1, 2, 3, 4)) => [1, 2]
     * }</pre>
     */
    public static <T>
    Iterable<T> take(final int num,
                     final Iterable<T> iterable) {
        requireNonNull(iterable);
        return () -> num < 1
                ? emptyIterator()
                : lazy(takeI(num, iterable.iterator())).iterator();
    }

    private static <T>
    Supplier<Continuation<T>> takeI(int num, Iterator<? extends T> iterator) {
        return () -> iterator.hasNext() && num > 0
                     ? seq(takeI(num - 1, iterator), iterator.next())
                     : stop();
    }
    /**
     * Iterable of lazy iterators having the first elements matching predicate
     * <p>Example:
     * <pre>{@code
     * takeWhile(x -> x<3, Arrays.asList(1, 2, 3, 4)) => [1, 2]
     * }</pre>
     */
    public static <T>
    Iterable<T> takeWhile(Predicate<? super T> pred,
                          Iterable<T> iterable) {
        requireNonNulls(pred,iterable);
        return () -> lazy(takeWhileI(pred,
                                     iterable.iterator())).iterator();

    }

    private static <T> Supplier<Continuation<T>> takeWhileI(Predicate<? super T> pred, Iterator<? extends T> iterator) {
        return () -> {
            if (!iterator.hasNext())
                return stop();
            T t = iterator.next();
            return pred.test(t)
                   ? seq(takeWhileI(pred, iterator), t)
                   : stop();
        };
    }
    /** Flattens Iterable<Iterable <T>> into Iterable<T> */
    public static <T>
    Iterable<T> join(Iterable<? extends Iterable<? extends T>> nested) {
        requireNonNull(nested);
        return flatMap(Function.identity(), nested);
    }

    /** creates a singleton list of a value, lifting the value into List context */
    public static <T>
    Iterable<T> unit(T value) {
        return Collections.singletonList(value);
    }

     /** Iterable of lazy iterators partitioning elements by application to function
      * <p>Example:
      * <pre>{@code
      *  partitionBy( v -> v%2, asList(1, 3, 4, 5, 7, 6)) => [[1, 3], [4], [5, 7], [6]]
      * }</pre>
      */
    public static <T>
    Iterable<List<T>> partitionBy(Function<? super T, Object> function,
                                  Iterable<T> iterable) {
        requireNonNulls(function, iterable);
        return () -> lazy(partitionByI(function,
                                       new Group<>(null, null),
                                       iterable.iterator())).iterator();
    }

    static private
    class Group<T> {
        final Object key;
        final List<T> values;

        Group(Object key, List<T> values) {
            this.key = key;
            this.values = values;
        }
    }

    private static <T>
    Supplier<Continuation<List<T>>> partitionByI(Function<? super T, Object> function,
                                                 Group<T> acc,
                                                 Iterator<T> it) {
        return () -> {
            if (it.hasNext()) {
                T value = it.next();
                Object key = function.apply(value);

                if (Objects.equals(acc.key, key)) {
                    List<T> current = acc.values != null ? acc.values : new ArrayList<>();
                    current.add(value);
                    return recur(partitionByI(function, new Group<>(key, current), it));
                }

                if (acc.values != null) {
                    List<T> newGroup = new ArrayList<>();
                    newGroup.add(value);
                    return seq(partitionByI(function, new Group<>(key, newGroup), it),
                            new ArrayList<>(acc.values)); // ← concrete copy
                }

                List<T> firstGroup = new ArrayList<>();
                firstGroup.add(value);
                return recur(partitionByI(function, new Group<>(key, firstGroup), it));
            }

            return acc.values == null
                    ? stop()
                    : done(new ArrayList<>(acc.values)); // ← also concrete
        };
    }
  /**
   * Iterable of lazy iterators but the num first elements of iterable
   */
    public static <T> Iterable<T> drop(int num, Iterable<T> iterable) {
        requireNonNull( iterable);
        return () ->
            (num < 1)
            ? iterable.iterator()
            : lazy(dropI(num, iterable.iterator())).iterator();

    }

    private static <T> Supplier<Continuation<T>> dropI(int num, Iterator<? extends T> i) {
        return () -> {
            if (0 < num) {
                if (i.hasNext()) {
                    i.next();
                    return recur(dropI(num - 1, i));
                }
                return stop();
            }
            if (i.hasNext()) {
                T v = i.next();
                return seq(dropI(0, i), v);
            }
            return stop();
        };
    }

    /** Iterable of lazy iterators emitting all elements but except the first matching predicate*/
    public static <T> Iterable<T> dropWhile(Predicate<? super T> predicate, Iterable<T> iterable) {
        requireNonNulls( iterable, predicate);
        return () -> lazy(dropWhileI(predicate, true, iterable.iterator())).iterator();
    }

    private static <T> Supplier<Continuation<T>> dropWhileI(Predicate<? super T> predicate, boolean dropping, Iterator<T> i){
        return ()->{
            if(i.hasNext()){
                T v=i.next();
                return (dropping && predicate.test(v))
                       ? recur(dropWhileI(predicate, true, i))
                       : seq(dropWhileI(predicate, false, i), v);
            }
            return stop();
        };
    }

}