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.*;
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.*;

/**
 * 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){
        List<T> l = new ArrayList<>(1);
        l.add(t0);
        return l;
    }
    private static <T> List<T> arrayList(
            T t0,
            T t1){
        List<T> l = new ArrayList<>(2);
        l.add(t0);
        l.add(t1);
        return l;
    }

    static <T> List<T> asList(
            T t0,
            T t1){
        List<T> l = arrayList(
                t0,
                t1
        );
        return Collections.unmodifiableList(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
     * * <p>Example:
     * <pre>{@code
     * fold((a,v)->a+v, 0, Arrays.asList(1,2,3,4)) => 10
     * }</pre>
     */
    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));
        };
    }
    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()));
    }
}