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

me.shaftesbury.utils.functional.Functional Maven / Gradle / Ivy

There is a newer version: 1.17
Show newest version
package me.shaftesbury.utils.functional;

import org.javatuples.Pair;
import org.javatuples.Triplet;

import java.util.*;

/**
 * Herein are contained some standard algorithms from functional programming.
 * See Functional Programming
 * for more information
 */
public final class Functional
{
    private Functional() {}

    /**
     * A simple predicate which checks the contents of the string parameter.
     * @param s the input string
     * @return true if s is either null or s is the empty string; false otherwise.
     */
    public final static boolean isNullOrEmpty(final String s)
    {
        return s==null || s.isEmpty();
    }

    /**
     * Concatenate all of the input elements into a single string where each element is separated from the next by the supplied delimiter
     * @param delimiter used to separate consecutive elements in the output
     * @param strs input sequence, each element of which must be convertible to a string
     * @param  the type of the element in the input sequence
     * @return a string containing the string representation of each input element separated by the supplied delimiter
     */
    public final static String join(final String delimiter, final Iterable strs)
    {
        if(strs==null) return "";
        final Iterator it = strs.iterator();
        final StringBuilder sb = new StringBuilder();
        boolean isFirst = true;
        while(it.hasNext())
        {
            if(!isFirst) sb.append(delimiter);
            sb.append(it.next());
            isFirst=false;
        }
        return sb.toString();
    }

    /**
     * A string function: generate a string that contains the 'unitOfIndentation' repeated 'howMany' times prepended to 'indentThis'
     * @param howMany times should the unitOfIndentation be prefixed to the supplied 'indentThis' string
     * @param unitOfIndentation the indentation
     * @param indentThis the input string that should be indented
     * @return a string indenting the input string by the indicated number of units
     */
    public final static String indentBy(final int howMany, final String unitOfIndentation, final String indentThis)
    {
        final Collection indentation = init(
                new Func() {
                    @Override
                    public String apply(final Integer integer) {
                        return unitOfIndentation;
                    }
                }, howMany);
        return fold(new Func2() {
            @Override
            public String apply(final String state, final String str) {
                return str + state;
            }
        }, indentThis, indentation);
    }

    /**
     * foldAndChoose: fold except that instead of folding every element in the input sequence, fold
     * only those for which the fold function 'f' returns a Some value (see Option)
     * @param f is the fold function modified such that the return value contains an Option in addition to the state
     * @param initialValue the seed for the fold function
     * @param input the input sequence
     * @param  the type of the initialValue / seed
     * @param  the type of the element in the input sequence
     * @return the folded value paired with those transformed elements which are Some
     * @throws OptionNoValueAccessException
     */
    public final static Pair> foldAndChoose(
            final Func2>> f,
            final A initialValue, final Iterable input) throws OptionNoValueAccessException
    {
        if (f == null) throw new IllegalArgumentException("f");
        if (input == null) throw new IllegalArgumentException("input");

        A state = initialValue;
        final List results = new ArrayList();
        for (final B b : input)
        {
            final Pair> intermediate = f.apply(state, b);
            state = intermediate.getValue0();
            if (!intermediate.getValue1().isNone())
                results.add(intermediate.getValue1().Some());
        }
        return new Pair>(state, Collections.unmodifiableList(results));
    }

    public static final List toList(final Enumeration input)
    {
        final List output = new ArrayList();
        while(input.hasMoreElements())
            output.add(input.nextElement());
        return Collections.unmodifiableList(output);
    }

    /**
     * Analogue of string.Join for List with the addition of a user-defined map function
     *
     * @param  the type of the element in the input sequence
     * @param separator inserted between each transformed element
     * @param l the input sequence
     * @param fn map function (see map) which is used to transform the input sequence
     * @return a string containing the transformed string value of each input element separated by the supplied separator
     */
    public final static String join(final String separator, final Iterable l, final Func fn)
    {
        if (l == null) throw new IllegalArgumentException("l");
        if (fn == null) throw new IllegalArgumentException("fn");

        return join(separator, map(fn,l));
    }

    /**
     * @param lowerBound
     * @param upperBound
     * @param val
     * @param  the type of the input element
     * @return lowerBound < val < upperBound
     */
    public final static >boolean between(final T lowerBound, final T upperBound, final T val)
    {
        if (val == null) throw new IllegalArgumentException("val");

        return val.compareTo(lowerBound) == 1 && val.compareTo(upperBound) == -1;
    }

    /**
     * Find the first element from the input sequence for which the supplied predicate returns true
     * find: (A -> bool) -> A list -> A
     * @param f predicate
     * @param input sequence
     * @param  the type of the element in the input sequence
     * @throws java.lang.IllegalArgumentException if f or input are null
     * @throws java.util.NoSuchElementException if no element is found that satisfies the predicate
     * @return the first element from the input sequence for which the supplied predicate returns true
     */
    public final static A find(final Func f, final Iterable input)
    {
        if (f == null) throw new IllegalArgumentException("f");
        if (input == null) throw new IllegalArgumentException("input");

        for(final A a : input)
            if(f.apply((a)))
                return a;
        throw new NoSuchElementException();
    }

    /**
     * Curried find.
     * Find the first element from the input sequence for which the supplied predicate returns true
     * find: (A -> bool) -> A list -> A
     * @param f predicate
     * @param  the type of the element in the input sequence
     * @throws java.lang.IllegalArgumentException if f or input are null
     * @throws java.util.NoSuchElementException if no element is found that satisfies the predicate
     * @return a curried function that expects an input sequence which it feeds to the predicate f
     *          which returns the first element from the input sequence for which the supplied predicate returns true
     * @see Currying
     */
    public final static Func,A> find(final Func f)
    {
        return new Func, A>() {
            @Override
            public A apply(final Iterable input) {
                return Functional.find(f,input);
            }
        };
    }

    /**
     * As find except that here we return the zero-based position in the input sequence of the found element
     * findIndex: (A -> bool) -> A list -> int
     * @param f predicate
     * @param input sequence
     * @param  the type of the element in the input sequence
     * @throws java.lang.IllegalArgumentException if f or input are null
     * @throws java.util.NoSuchElementException if no element is found that satisfies the predicate
     * @return the position in the input sequence of the first element from the input sequence for which the supplied predicate
     * returns true
     */
    public static int findIndex(final Func f, final Iterable input)
    {
        if (f == null) throw new IllegalArgumentException("f");
        if (input == null) throw new IllegalArgumentException("input");

        int pos = 0;
        for (final A a : input)
            if (f.apply(a))
                return pos;
            else pos++;
        throw new IllegalArgumentException();
    }

    /**
     * As find except that here we return the last element in the input sequence that satisfies the predicate 'f'
     * findLast: (A -> bool) -> A seq -> A
     * @param f predicate
     * @param input sequence
     * @param  the type of the element in the input sequence
     * @throws java.lang.IllegalArgumentException if f or input are null
     * @throws java.util.NoSuchElementException if no element is found that satisfies the predicate
     * @return the last element in the input sequence for which the supplied predicate returns true
     */
    public final static A findLast(final Func f, final Iterable input)
    {
        if (f == null) throw new IllegalArgumentException("f");
        if (input == null) throw new IllegalArgumentException("input");

        final Pair,Iterable> p = takeNAndYield(input,1);
        final Pair seed = Pair.with(p.getValue0().get(0),f.apply(p.getValue0().get(0)));
        final Pair result = fold(new Func2,A,Pair>(){
            @Override public Pair apply(final Pair state, final A item){return f.apply(item)?Pair.with(item,true):state;}
        },seed,p.getValue1());

        if(result.getValue1()) return result.getValue0();
        throw new NoSuchElementException();
    }

    /**
     * As find except that here we return the last element in the input sequence that satisfies the predicate 'f'
     * findLast: (A -> bool) -> A list -> A
     * @param f predicate
     * @param input sequence
     * @param  the type of the element in the input sequence
     * @throws java.lang.IllegalArgumentException if f or input are null
     * @throws java.util.NoSuchElementException if no element is found that satisfies the predicate
     * @return the last element in the input sequence for which the supplied predicate returns true
     */
    public final static A findLast(final Func f, final List input)
    {
        if (f == null) throw new IllegalArgumentException("f");
        if (input == null) throw new IllegalArgumentException("input");

        for (final A a : Iterators.reverse(input))
            if (f.apply(a))
                return a;
        throw new NoSuchElementException();
    }

    /**
     * A curried version of findLast.
     * As find except that here we return the last element in the input sequence that satisfies the predicate 'f'
     * findLast: (A -> bool) -> A list -> A
     * @param f predicate
     * @param  the type of the element in the input sequence
     * @throws java.lang.IllegalArgumentException if f or input are null
     * @throws java.util.NoSuchElementException if no element is found that satisfies the predicate
     * @return the last element in the input sequence for which the supplied predicate returns true
     * @see Currying
     */
    public final static Func,A> findLast(final Func f)
    {
        return new Func, A>() {
            @Override
            public A apply(final List input) {
                return Functional.findLast(f,input);
            }
        };
    }

    /**
     * 'pick' is an analogue of find. Instead of a predicate, 'pick' is passed a map function which returns an Option.
     * Each element of the input sequence is supplied in turn to the map function 'f' and the first non-None Option to be returned from
     * the map function is returned by 'pick' to the calling code.
     * pick: (A -> B option) -> A seq -> B
     *
     * @param f the map function.
     * @param input the input sequence
     * @param  the type of the element in the input sequence
     * @param  the type of the output element
     * @return the first non-None transformed element of the input sequence
     */
    public static B pick(final Func> f, final Iterable input)
    {
        if (f == null) throw new IllegalArgumentException("f");
        if (input == null) throw new IllegalArgumentException("input");

        for(final A a : input)
        {
            final Option intermediate = f.apply(a); // which is, effectively, if(f(a)) return f(a), but without evaluating f twice
            if (!intermediate.isNone())
                return intermediate.Some();
        }
        throw new NoSuchElementException();
    }

    /**
     * 'pick' is an analogue of find. Instead of a predicate, 'pick' is passed a map function which returns an Option.
     * Each element of the input sequence is supplied in turn to the map function 'f' and the first non-None Option to be returned from
     * the map function is returned by 'pick' to the calling code.
     *
     * This is a curried implementation of 'pick'
     *
     * pick: (A -> B option) -> A seq -> B
     *
     * @param f the map function.
     * @param  the type of the element in the input sequence
     * @param  the type of the output element
     * @return the first non-None transformed element of the input sequence
     * @see Currying
     */
    public static Func,B> pick(final Func> f)
    {
        return new Func, B>() {
            @Override
            public B apply(final Iterable input) {
                return Functional.pick(f,input);
            }
        };
    }

    /**
     * In, used for functional composition. This is the simple reversal function. y(x) is equivalent to x.In(y)
     * See Function Composition
     * @param input the object which we wish to pass to the function parameter
     * @param f the function we wish to evaluate
     * @param  the base type of the input element. That is AA extends A
     * @param  the type of the output of the function f
     * @param  the type of the input
     * @return f(input)
     */
    public final static  B in(final AA input, final Func f)
    {
        return f.apply(input);
    }

    /**
     * Then, the functional composition operator. Execute the first function then execute the second, passing the results
     * of the first as the input to the second.
     * See Function Composition
     * @param f the first function to execute.
     * @param g the second function to execute. The input to this function will be the result of the first function, f
     * @param  the type of the input to f
     * @param  the type of the input to g and a base class of the output of f
     * @param  the type of the output of g
     * @return a function equivalent to g(f(x))
     */
    public final static  Func then(final Func f, final Func g)
    {
        return new Func()
        {
            @Override
            public C apply(final A x)
            {
                return g.apply(f.apply(x));
            }
        };
    }

    /**
     * The identity transformation function: that is, the datum supplied as input is returned as output
     * @param  the type of the input element
     * @return a function which is the identity transformation
     */
    public final static Func identity()
    {
        return new Func() {
            @Override
            public T apply(final T t) {
                return t;
            }
        };
    }

    /**
     * isEven a function that accepts an integer and returns a boolean that indicates whether the passed integer
     * is or is not an even integer
     */
    public static final Func isEven = new Func()
    {
        @Override
        public Boolean apply(final Integer i)
        {
            return i % 2 == 0;
        }
    };
    /**
     * isOdd a function that accepts an integer and returns a boolean that indicates whether the passed integer
     * is or is not an odd integer
     */
    public static final Func isOdd = new Func()
    {
        @Override
        public Boolean apply(final Integer i)
        {
            return i % 2 != 0;
        }
    };
    /**
     * count a function that accepts a counter and another integer and returns 1 + counter
     */
    public static final Func2 count = new Func2() {
                @Override
                public Integer apply(final Integer state, final Integer b) {
                    return state + 1;
                }
            };
    /**
     * sum a function that accepts two integers and returns the sum of them
     */
    public static final Func2 sum = new Func2() {
        @Override
        public Integer apply(final Integer state, final Integer b) {
            return state + b;
        }
    };

    /**
     * @param  the type of that, the input argument
     * @return a function that compares its supplied argument with the 'that' argument and returns true if 'this' is greater than
     * 'that' or false otherwise
     */
    public static final >Func greaterThan(final T that)
    {
        return new Func()
        {
            @Override
            public Boolean apply(final T ths)
            {
                return ths.compareTo(that)>0;
            }
        };
    }

    /**
     * @param  the type of that, the input argument
     * @return a function that compares its supplied argument with the 'that' argument and returns true if 'this' is greater than
     * or equal to 'that' or false otherwise
     */
    public static final >Func greaterThanOrEqual(final T that)
    {
        return new Func()
        {
            @Override
            public Boolean apply(final T ths)
            {
                return ths.compareTo(that)>=0;
            }
        };
    }

    /**
     * @param  the type of that, the input argument
     * @return a function that compares its supplied argument with the 'that' argument and returns true if 'this' is less than
     * 'that' or false otherwise
     */
    public static final >Func lessThan(final T that)
    {
        return new Func()
        {
            @Override
            public Boolean apply(final T ths)
            {
                return ths.compareTo(that)<0;
            }
        };
    }

    /**
     * @param  the type of that, the input argument
     * @return a function that compares its supplied argument with the 'that' argument and returns true if 'this' is less than
     * or equal to 'that' or false otherwise
     */
    public static final >Func lessThanOrEqual(final T that)
    {
        return new Func()
        {
            @Override
            public Boolean apply(final T ths)
            {
                return ths.compareTo(that)<=0;
            }
        };
    }

    /**
     * The init function, not dissimilar to list comprehensions, which is used to return a new finite list whose contents are
     * determined by successive calls to the function f.
     * init: (int -> A) -> int -> A list
     * @param f generator function used to produce the individual elements of the output list. This function is called by init
     *          with the unity-based position of the current element in the output list being produced. Therefore, the first time
     *          f is called it will receive a literal '1' as its argument; the second time '2'; etc.
     * @param howMany the number of elements in the output list
     * @param  the type of the element in the output sequence
     * @return a list of 'howMany' elements of type 'T' which were generated by the function 'f'
     */
    public final static List init(final Func f,final int howMany)
    {
        if (f == null) throw new IllegalArgumentException("f");
        if(howMany<1) throw new IllegalArgumentException("howMany");

        final List output = new ArrayList(howMany);
        for(int i=1; i<=howMany; ++i)
            output.add(f.apply(i));
        return Collections.unmodifiableList(output);
    }

    /**
     * See Map
     * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into an element in the output sequence.
     * map: (A -> B) -> A list -> B list
     * @param f a transformation function which takes a object of type A and returns an object, presumably related, of type B
     * @param input a sequence to be fed into f
     * @param  the type of the element in the input sequence
     * @param  the type of the element in the output sequence
     * @return a list of type B containing the transformed values.
     */
    public final static  List map(final Func f, final Iterable input)
    {
        final List output = input instanceof Collection ? new ArrayList(((Collection) input).size()) : new ArrayList();
        for(final A a : input)
            output.add(f.apply(a));
        return Collections.unmodifiableList(output);
    }

    /**
     * See Map
     * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into an element in the output sequence.
     * map: (A -> B) -> A list -> B list
     * @param f a transformation function which takes a object of type A and returns an object, presumably related, of type B
     * @param  the type of the element in the input sequence
     * @param  the type of the element in the output sequence
     * @return a curried function that expects an input sequence which it feeds to the transformation f which returns a list of type B
     *          containing the transformed values.
     * @see Currying
     */
    public final static  Func,List> map(final Func f)
    {
        return new Func, List>() {
            @Override
            public List apply(final Iterable input) {
                return Functional.map(f,input);
            }
        };
    }

    /**
     * See Map
     * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into an element in the output sequence.
     * mapi: (int -> A -> B) -> A list -> B list
     * @param f a transformation function which is passed each input object of type A along with its position in the input sequence
     *          (starting from zero) and returns an object, presumably related, of type B
     * @param input a sequence to be fed into f
     * @param  the type of the element in the input sequence
     * @param  the type of the element in the output sequence
     * @return a list of type B containing the transformed values.
     */
    public final static  List mapi(final Func2 f, final Iterable input)
    {
        final List output = input instanceof Collection ? new ArrayList(((Collection) input).size()) : new ArrayList();
        int pos = 0;
        for(final A a : input)
            output.add(f.apply(pos++, a));
        return Collections.unmodifiableList(output);
    }

    /**
     * See Map
     * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into an element in the output sequence.
     * mapi: (int -> A -> B) -> A list -> B list
     * @param f a transformation function which is passed each input object of type A along with its position in the input sequence
     *          (starting from zero) and returns an object, presumably related, of type B
     * @param  the type of the element in the input sequence
     * @param  the type of the element in the output sequence
     * @return a curried function that expects an input sequence which it feeds to the transformation f which returns a list of type B
     *          containing the transformed values.
     * @see Currying
     */
    public final static  Func,List> mapi(final Func2 f)
    {
        return new Func, List>() {
            @Override
            public List apply(final Iterable input) {
                return Functional.mapi(f,input);
            }
        };
    }

    ///  sortWith: (A -> A -> int) -> A list -> A list

    /**
     * sortWith: a wrapper for Collections.sort which preserves the input sequence.
     * @param f the Comparator to use for the sort
     * @param input the input
     * @param  the type of the Comparator
     * @param  the type of the element in the input sequence
     * @return a sorted list containing all the elements of 'input' sorted using Collections.sort and 'f'
     */
    public final static List sortWith(final Comparator f, final List input)
    {
        final List output = new ArrayList(input);
        Collections.sort(output, f);
        return Collections.unmodifiableList(output);
    }

    /**
     * A simple function which wraps left.compareTo(right) so that this can be used as a sort function.
     * @param left input element
     * @param right input element
     * @param  the type of the elements to be compared
     * @return left.compareTo(right)
     */
    public final static >int Sorter(final A left, final A right)
    {
        return left.compareTo(right);
    }

    /**
     * A Comparator that encapsulates Sorter above
     */
    public final static Comparator dSorter = new Comparator()
    {
        @Override public int compare(final Integer i, final Integer j) { return Sorter(i, j); }
    };

    /**
     * A wrapper around toString()
     * @param a the element to be turned into a string using T.toString()
     * @param  the type of element 'a'
     * @return a.toString()
     */
    public final static  String Stringify(final T a) { return a.toString(); }

    /**
     * A transformation function that wraps Stringify
     * @param  the type of the element which we will render as a String
     * @return a function that calls Stringify
     */
    public final static Func dStringify()
    {
        return new Func()
        {
            @Override public String apply(final T i) { return Stringify(i); }
        };
    }

    /**
     * forAll2: the predicate 'f' is applied to all elements in the input sequences input1 and input2 as pairs. If the predicate returns
     * true for all pairs and there is the same number of elements in both input sequences then forAll2 returns true. If the predicate
     * returns false at any point then the traversal of the input sequences halts and forAll2 returns false.
     * forAll2: (A -> B -> bool) -> A list -> B list -> bool
     * @param f predicate to which each successive pair (input1_i, input2_i) is applied
     * @param input1 input sequence
     * @param input2 input sequence
     * @param  the base type of the element in the first input sequence
     * @param  the base type of the element in the second input sequence
     * @param  the type of the element in the first input sequence
     * @param  the type of the element in the second input sequence
     * @return true if the predicate 'f' evaluates true for all pairs, false otherwise
     * @throws java.lang.IllegalArgumentException if the predicate returns true for all pairs and the sequences contain differing numbers
     * of elements
     */
    public final static boolean forAll2(final Func2 f, final Iterable input1, final Iterable input2)
    {
        final Iterator enum1 = input1.iterator();
        final Iterator enum2 = input2.iterator();
        boolean enum1Moved = false, enum2Moved = false;
        do
        {
            enum1Moved = enum1.hasNext();
            enum2Moved = enum2.hasNext();
            if (enum1Moved && enum2Moved && !f.apply(enum1.next(), enum2.next()))
                return false;
        } while (enum1Moved && enum2Moved);
        if( enum1Moved != enum2Moved)
            throw new IllegalArgumentException();
        return true;
    }

    /**
     * See Filter
     * @param pred a filter function. This is passed each input element in turn and returns either true or false. If true then
     *             the input element is passed through to the output otherwise it is ignored.
     * @param input a sequence of objects
     * @param  the type of the element in the input sequence
     * @return a list which contains zero or more of the elements of the input sequence. Each element is included only if the filter
     *          function returns true for the element.
     */
    public final static List filter(final Func pred, final Iterable input)
    {
        final List output = input instanceof Collection ? new ArrayList(((Collection) input).size()) : new ArrayList();
        for(final A element : input)
            if(pred.apply(element))
                output.add(element);

        return Collections.unmodifiableList(output);
    }

    /**
     * See Filter
     * @param f a filter function. This is passed each input element in turn and returns either true or false. If true then
     *             the input element is passed through to the output otherwise it is ignored.
     * @param  the type of the element in the input sequence
     * @return a curried function that expects an input sequence which it feeds to the filter predicate which then returns
     *          a list which contains zero or more of the elements of the input sequence. Each element is included only if the filter
     *          function returns true for the element.
     * @see Currying
     */
    public static final Func,List> filter(final Func f)
    {
        return new Func, List>() {
            @Override
            public List apply(final Iterable input) {
                return Functional.filter(f,input);
            }
        };
    }

    /**
     * The converse operation to forAll. If the predicate returns true then 'exists' returns true and halts the traveral of the
     * input sequence. Otherwise return false.
     * exists: (A -> bool) -> A list -> bool
     * @param f predicate
     * @param input input sequence
     * @param  the type of the element in the input sequence
     * @return true if the predicate returns true for any element in the input sequence, false otherwise
     */
    public final static boolean exists(final Func f, final Iterable input)
    {
        for(final A a : input)
            if(f.apply(a))
                return true;
        return false;
    }

    /**
     * The converse operation to forAll. If the predicate returns true then 'exists' returns true and halts the traveral of the
     * input sequence. Otherwise return false.
     * exists: (A -> bool) -> A list -> bool
     * This is the curried implementation.
     * @param f predicate
     * @param  the type of the element in the input sequence
     * @return true if the predicate returns true for any element in the input sequence, false otherwise
     * @see Currying
     */
    public final static Func,Boolean> exists(final Func f)
    {
        return new Func, Boolean>() {
            @Override
            public Boolean apply(final Iterable input) {
                return Functional.exists(f,input);
            }
        };
    }

    /**
     * not reverses the result of the applied predicate
     * not: (A -> bool) -> (A -> bool)
     * @param f the applied predicate
     * @param  the type of the input to the function f
     * @return true if f returns false, false if f returns true
     */
    public final static Func not(final Func f)
    {
        return new Func(){@Override public Boolean apply(final A a) { return !f.apply(a);}};
    }

    /**
     * The converse operation to exists. If the predicate returns true for all elements in the input sequence then 'forAll'
     * returns true otherwise return false.
     * forAll: (A -> bool) -> A list -> bool
     * @param f predicate
     * @param input input sequence
     * @param  the type of the element in the input sequence
     * @return true if the predicate returns true for all elements in the input sequence, false otherwise
     */
    public final static boolean forAll(final Func f, final Iterable input)
    {
        return !exists(not(f), input);
    }

    /**
     * The converse operation to exists. If the predicate returns true for all elements in the input sequence then 'forAll'
     * returns true otherwise return false.
     * forAll: (A -> bool) -> A list -> bool
     * This is a curried implementation of 'forAll
     * @param f predicate
     * @param  the type of the element in the input sequence
     * @return true if the predicate returns true for all elements in the input sequence, false otherwise
     * @see Currying
     */
    public final static Func,Boolean> forAll(final Func f)
    {
        return new Func, Boolean>() {
            @Override
            public Boolean apply(final Iterable input) {
                return Functional.forAll(f,input);
            }
        };
    }

    /**
     * not2 reverses the result of the applied predicate
     * not2: (A -> B -> bool) -> (A -> B -> bool)
     * @param f the applied predicate
     * @param  the type of the first input to the function f
     * @param  the type of the second input to the function f
     * @return true if f returns false, false if f returns true
     */
    public final static  Func2 not2(final Func2 f)
    {
        return new Func2(){@Override public Boolean apply(final A a, final B b) { return !f.apply(a,b);}};
    }

    ///  
    ///  (list * list). The first list contains all items for which f(a) is true. The second list contains the remainder.

    /**
     * partition is a group function. Given a predicate and an input sequence, 'partition' returns a pair of lists, the first list
     * containing those elements from the input sequence for which the predicate returned true, the second list containing those
     * elements from the input sequence for which the predicate returned false.
     * partition: (A -> bool) -> A list -> A list * A list
     * @param f predicate used to split the input sequence into two groups
     * @param input the input sequence
     * @param  the type of the element in the input sequence
     * @return a pair of lists, the first being the 'true' and the second being the 'false'
     */
    public final static Pair,List> partition(final Func f, final Iterable input)
    {
        final List left;
        final List right;
        if(input instanceof Collection)
        {
            left = new ArrayList(((Collection) input).size());
            right = new ArrayList(((Collection) input).size());
        }
        else
        {
            left = new ArrayList();
            right = new ArrayList();
        }
        for (final A a : input)
            if (f.apply(a))
                left.add(a);
            else
                right.add(a);
        return new Pair,List>(Collections.unmodifiableList(left), Collections.unmodifiableList(right));
    }

    /**
     * partition is a group function. Given a predicate and an input sequence, 'partition' returns a pair of lists, the first list
     * containing those elements from the input sequence for which the predicate returned true, the second list containing those
     * elements from the input sequence for which the predicate returned false.
     * partition: (A -> bool) -> A list -> A list * A list
     * This is a curried implementation of 'forAll
     * @param f predicate used to split the input sequence into two groups
     * @param  the type of the element in the input sequence
     * @return a pair of lists, the first being the 'true' and the second being the 'false'
     * @see Currying
     */
    public final static Func,Pair,List>> partition(final Func f)
    {
        return new Func, Pair, List>>() {
            @Override
            public Pair, List> apply(final Iterable input) {
                return Functional.partition(f,input);
            }
        };
    }

    /**
     * choose: this is a map transformation with the difference being that the number of elements in the output sequence may
     * be between zero and the number of elements in the input sequence.
     * See Map
     * choose: (A -> B option) -> A list -> B list
     * @param f map function. This transforms the input element into an Option
     * @param input input sequence
     * @param  the type of the element in the input sequence
     * @param  the type of the element in the output sequence
     * @return a list of transformed elements, numbering less than or equal to the number of input elements
     */
    public final static List choose(final Func> f, final Iterable input)
    {
        final List results = input instanceof Collection ? new ArrayList(((Collection) input).size()) : new ArrayList();
        for(final A a : input)
        {
            final Option intermediate = f.apply(a);
            if (!intermediate.isNone())
                results.add(intermediate.Some());
        }
        return Collections.unmodifiableList(results);
    }

    /**
     * choose: this is a curried implementation of choose.
     * choose is a map transformation with the difference being that the number of elements in the output sequence may
     * be between zero and the number of elements in the input sequence.
     * See Map
     * choose: (A -> B option) -> A list -> B list
     * @param f map function. This transforms the input element into an Option
     * @param  the type of the element in the input sequence
     * @param  the type of the element in the output sequence
     * @return a list of transformed elements, numbering less than or equal to the number of input elements
     * @see Currying
     */
    public final static Func,List> choose(final Func> f)
    {
        return new Func, List>() {
            @Override
            public List apply(final Iterable input) {
                return Functional.choose(f,input);
            }
        };
    }

    /**
     * See Fold
     * fold: aggregate the elements of the input sequence given a seed and an aggregation function.
     * fold: (A -> B -> A) -> A -> B list -> A
     * @param f aggregation function
     * @param initialValue seed for the algorithm
     * @param input input sequence
     * @param  the type of the initialValue / seed
     * @param  the type of the element in the input sequence
     * @return aggregated value
     */
    public final static A fold(final Func2 f, final A initialValue, final Iterable input)
    {
        A state = initialValue;
        for (final B b : input)
            state = f.apply(state, b);
        return state;
    }

    /**
     * See Fold
     * fold: aggregate the elements of the input sequence given a seed and an aggregation function.
     * This is the curried implementation
     * fold: (A -> B -> A) -> A -> B list -> A
     * @param f aggregation function
     * @param initialValue seed for the algorithm
     * @param  the type of the initialValue / seed
     * @param  the type of the element in the output sequence
     * @return aggregated value
     * @see Currying
     */
    public final static Func,A> fold(final Func2 f, final A initialValue)
    {
        return new Func, A>() {
            @Override
            public A apply(final Iterable input) {
                return Functional.fold(f,initialValue,input);
            }
        };
    }

    /**
     * See Unfold and
     * Anamorphism
     * This is the converse of fold
     * unfold: (b -> (a, b)) -> (b -> Bool) -> b -> [a]
     */
    public final static List unfold(final Func> unspool, final Func finished, final B seed)
    {
        if(unspool==null) throw new IllegalArgumentException("unspool");
        if(finished==null) throw new IllegalArgumentException("finished");

        B next = seed;
        final List results = new ArrayList();
        while(!finished.apply(next)) {
            final Pair t = unspool.apply(next);
            results.add(t.getValue0());
            next = t.getValue1();
        }
        return results;
    }

    /**
     * See Unfold
     * and Anamorphism
     * This is the converse of fold
     * unfold: (b -> (a, b)) -> (b -> Bool) -> b -> [a]
     */
    public final static List unfold(final Func>> unspool, final B seed)
    {
        if(unspool==null) throw new IllegalArgumentException("unspool");

        B next = seed;
        final List results = new ArrayList();
        while(true) {
            final Option> t = unspool.apply(next);
            if(t.isNone()) break;
            results.add(t.Some().getValue0());
            next = t.Some().getValue1();
        }
        return results;
    }

    /**
     * toDictionary: given each element from the input sequence apply the keyFn and valueFn to generate a (key,value) pair.
     * The resulting dictionary (java.util.Map) contains all these pairs.
     * @param keyFn function used to generate the key
     * @param valueFn function used to generate the value
     * @param input input sequence
     * @param  the type of the element in the input sequence
     * @param  the type of the key elements
     * @param  the type of the value elements
     * @return a java.util.Map containing the transformed input sequence
     * @throws IllegalArgumentException if some property of the specified key
     *         or value prevents it from being stored in this map
     */
    public final static Map toDictionary(final Func keyFn, final Func valueFn, final Iterable input)
    {
        if(keyFn==null) throw new IllegalArgumentException("keyFn");
        if(valueFn==null) throw new IllegalArgumentException("valueFn");

        final Map output = new HashMap();
        for(final T element : input) output.put(keyFn.apply(element), valueFn.apply(element));
        return Collections.unmodifiableMap(output);
    }

    /**
     * toArray: create an array containing all the objects in the input sequence
     * @param input input sequence
     * @param  the type of the element in the input sequence
     * @return an array containing all the elements of the input sequence
     */
    public final static Object[] toArray(final Iterable input)
    //public final static T[] toArray(final Iterable input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.toArray(Iterable): input is null");

        if(input instanceof Collection)
            return ((Collection)input).toArray();

        final List output = new ArrayList();
        for(final T element: input) output.add(element);

        return output.toArray(); // this needs to be output.toArray(new T[0]) but that doesn't appear to be allowable Java :-(
    }

    public static final List toMutableList(final Iterable input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.toMutableList(Iterable): input is null");

        if(input instanceof Collection)
        {
            final Collection input_ = (Collection)input;
            final List output = new ArrayList(input_.size());
            output.addAll(input_);
            return output;
        }

        final List output = new ArrayList();
        for(final T element: input) output.add(element);

        return output;
    }

    public static final Map toMutableDictionary(final Map input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.toMutableDictionary(Map): input is null");

        final Map output = new HashMap(input.size());
        output.putAll(input);
        return output;
    }

    public static final Set toMutableSet(final Iterable input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.toMutableSet(Iterable): input is null");

        if(input instanceof Collection)
        {
            final Collection input_ = (Collection)input;
            final Set output = new HashSet(input_.size());
            output.addAll(input_);
            return output;
        }

        final Set output = new HashSet();
        for(final T element: input) output.add(element);

        return output;
    }

    /**
     * Create a java.util.List which contains all of the elements in the input sequence
     * @param input input sequence
     * @param  the type of the element in the input sequence
     * @return a list containing the elements of the input sequence
     */
    public static final List toList(final Iterable input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.toList(Iterable): input is null");
        return Collections.unmodifiableList(toMutableList(input));
    }

    /**
     * Create a java.util.Set which contains all of the elements in the input sequence
     * @param input input sequence
     * @param  the type of the element in the input sequence
     * @return a set containing the elements of the input sequence
     */
    public static final Set toSet(final Iterable input)
    {
        //Sets.newSetFromMap();
        if(input==null) throw new IllegalArgumentException("Functional.toSet(Iterable): input is null");
        return Collections.unmodifiableSet(toMutableSet(input));
    }

    /**
     * Return the final element from the input sequence
     * @param input input sequence
     * @param  the type of the element in the input sequence
     * @return the last element from the input sequence
     */
    public static final T last(final Iterable input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.last(Iterable): input is null");

        T state = null;
        for(final T element: input) state = element;

        return state;
    }

    /**
     * Return the final element from the input array
     * @param input input array
     * @param  the type of the element in the input sequence
     * @return the last element from the input array
     */
    public static final T last(final T[] input)
    {
        if(input==null||input.length==0) throw new IllegalArgumentException("Functional.last(T[]): input is null or empty");

        return input[input.length-1];
    }

    /**
     * Concatenate two sequences and return a new list containing the concatenation.
     * @param list1 first input sequence
     * @param list2 second input sequence
     * @param  the type of the element in the input sequences
     * @return a list containing the elements of the first sequence followed by the elements of the second sequence
     */
    public static final List concat(final Iterable list1, final Iterable list2)
    {
        if(list1==null) throw new IllegalArgumentException("Functional.concat(List,List): list1 is null");
        if(list2==null) throw new IllegalArgumentException("Functional.concat(List,List): list2 is null");

        final List newList = new ArrayList(toList(list1));
        final boolean didItChange = newList.addAll(toList(list2));
        return Collections.unmodifiableList(newList);
    }

    /**
     * take: given a list return another list containing the first 'howMany' elements
     * @param howMany a positive number of elements to be returned from the input sequence
     * @param list the input sequence
     * @param  the type of the element in the input sequence
     * @return a list containing the first 'howMany' elements of 'list'
     * @throws java.util.NoSuchElementException if more elements are requested than are present in the input sequence
     */
    public static finalList take(final int howMany, final Iterable list)
    {
        if(howMany<0) throw new IllegalArgumentException("Functional.take(int,Iterable): howMany is negative");
        if(list==null) throw new IllegalArgumentException("Functional.take(int,Iterable): list is null");

        if(howMany==0) return new ArrayList(0);

        final List output = new ArrayList(howMany);
        final Iterator iterator = list.iterator();
        for(int i=0;i the type of the element in the input sequence
     * @return a list containing the first 'howMany' elements of 'list'
     * @throws java.util.NoSuchElementException if more elements are requested than are present in the input sequence
     * @see Currying
     */
    public static finalFunc,List> take(final int howMany)
    {
        return new Func, List>() {
            @Override
            public List apply(final Iterable input) {
                return Functional.take(howMany,input);
            }
        };
    }

    /**
     * skip: the converse of take. Given a list return another list containing those elements that follow the
     * first 'howMany' elements. That is, if we skip(1,[1,2,3]) then we have [2,3]
     * @param howMany a non-negative number of elements to be discarded from the input sequence
     * @param list the input sequence
     * @param  the type of the element in the input sequence
     * @return a list containing the remaining elements after the first 'howMany' elements of 'list' or an empty list if more elements
     * are skipped than are present in the 'list'
     */
    public static final List skip(final int howMany, final List list)
    {
        if(howMany<0) throw new IllegalArgumentException("Functional.skip(int,List): howMany is negative");
        if(list==null) throw new IllegalArgumentException("Functional.skip(int,List): list is null");

        if(howMany==0) return Collections.unmodifiableList(list);
        final int outputListSize = list.size()-howMany;
        if(outputListSize<=0) return new ArrayList();

        return Collections.unmodifiableList(list.subList(howMany,list.size()));
    }

    /**
     * skip: the converse of take. Given a list return another list containing those elements that follow the
     * first 'howMany' elements. That is, if we skip(1,[1,2,3]) then we have [2,3]
     * This is the curried implementation
     * @param howMany a non-negative number of elements to be discarded from the input sequence
     * @param  the type of the element in the input sequence
     * @return a list containing the remaining elements after the first 'howMany' elements of 'list' or an empty list if more elements
     * are skipped than are present in the 'list'
     * @see Currying
     */
    public static finalFunc,List> skip(final int howMany)
    {
        return new Func, List>() {
            @Override
            public List apply(final List input) {
                return Functional.skip(howMany,input);
            }
        };
    }

    /**
     * constant: a function that returns a map function f(n) that returns the supplied 'constant'. Typically this would be
     * used in init
     * @param constant the desired constant value to be returned
     * @param  the type of the constant
     * @return a function that returns a function that returns the supplied constant
     */
    public static final Func constant(final T constant)
    {
        return new Func() {
            @Override
            public T apply(final Integer integer) {
                return constant;
            }
        };
    }

    /**
     * range: a function that returns a map function f(n) that returns an integer from the open-ended range [startFrom+n, infinity).
     * Typically this would be used in init
     * @param startFrom the lower bound of the range
     * @return a function that returns a function that returns an integer from the range [startFrom+n, infinity)
     */
    public static final Func range(final Integer startFrom)
    {
        return new Func(){
            private final Integer start = startFrom;
            public Integer apply(final Integer input) {
                return (start-1)+input; // because init starts counting from 1
            }
        };
    }

    /**
     * The Convolution operator
     * See Zip
     * @param l1 input sequence
     * @param l2 input sequence
     * @param  the type of the element in the first input sequence
     * @param  the type of the element in the second input sequence
     * @throws java.lang.IllegalArgumentException if either input sequence is null or if the sequences have differing lengths.
     * @return list of pairs; the first element from each of the two input sequences is the first pair in the output sequence and so on,
     *          in order. If the sequences do not have the same number of elements then an exception is thrown.
     */
    public static final List> zip(final Iterable l1, final Iterable l2)
    {
        if(l1==null) throw new IllegalArgumentException("Functional.zip(Iterable,Iterable): l1 is null");
        if(l2==null) throw new IllegalArgumentException("Functional.zip(Iterable,Iterable): l2 is null");

        final List> output;
        if(l1 instanceof Collection && l2 instanceof Collection) {
            if (((Collection) l1).size() != ((Collection) l2).size())
                throw new IllegalArgumentException("Functional.zip(Iterable,Iterable): l1 and l2 have differing numbers of elements");

            output = new ArrayList>(((Collection) l1).size());
        }
        else output = new ArrayList>();
        final Iterator l1_it = l1.iterator();
        final Iterator l2_it = l2.iterator();

        while(l1_it.hasNext() && l2_it.hasNext()) output.add(new Pair(l1_it.next(),l2_it.next()));
        if(l1_it.hasNext() || l2_it.hasNext()) throw new IllegalArgumentException("Functional.zip(Iterable,Iterable): l1 and l2 have differing numbers of elements");

        return Collections.unmodifiableList(output);
    }

    /**
     * The Convolution operator
     * See Zip
     * @param l1 input sequence
     * @param l2 input sequence
     * @param l3 input sequence
     * @param  the type of the element in the first input sequence
     * @param  the type of the element in the second input sequence
     * @param  the type of the element in the third input sequence
     * @throws java.lang.IllegalArgumentException if any input sequence is null or if the sequences have differing lengths.
     * @return list of triplets; the first element from each of the input sequences is the first triplet in the output sequence and so on,
     *          in order. If the sequences do not have the same number of elements then an exception is thrown.
     */
    public static final List> zip3(final Iterable l1, final Iterable l2, final Iterable l3)
    {
        if(l1==null) throw new IllegalArgumentException("Functional.zip3(Iterable,Iterable,Iterable): l1 is null");
        if(l2==null) throw new IllegalArgumentException("Functional.zip3(Iterable,Iterable,Iterable): l2 is null");
        if(l3==null) throw new IllegalArgumentException("Functional.zip3(Iterable,Iterable,Iterable): l3 is null");

        final List> output;
        if(l1 instanceof Collection && l2 instanceof Collection && l3 instanceof Collection) {
            if (((Collection) l1).size() != ((Collection) l2).size())
                throw new IllegalArgumentException("Functional.zip3(Iterable,Iterable,Iterable): l1, l2 and l3 have differing numbers of elements");

            output = new ArrayList>(((Collection) l1).size());
        }
        else output = new ArrayList>();
        final Iterator l1_it = l1.iterator();
        final Iterator l2_it = l2.iterator();
        final Iterator l3_it = l3.iterator();

        while(l1_it.hasNext() && l2_it.hasNext() && l3_it.hasNext()) output.add(new Triplet(l1_it.next(),l2_it.next(),l3_it.next()));
        if(l1_it.hasNext() || l2_it.hasNext() || l3_it.hasNext())
            throw new IllegalArgumentException("Functional.zip3(Iterable,Iterable,Iterable): l1, l2 and l3 have differing numbers of elements");

        return Collections.unmodifiableList(output);
    }

    /**
     * The converse of the Convolution operator
     * See Zip
     * @param input sequence of pairs
     * @param  the type of the first element in the pair
     * @param  the type of the second element in the pair
     * @throws java.lang.IllegalArgumentException if the input sequence is null
     * @return pair of lists; the first element from each of the two output sequences is the first pair in the input sequence and so on,
     *          in order.
     */
    public static final Pair,List> unzip(final Iterable> input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.unzip(Iterable>): input is null");

        final List l1;
        final List l2;
        if(input instanceof Collection) {
            final int size = ((Collection) input).size();
            l1 = new ArrayList(size);
            l2 = new ArrayList(size);
        }
        else {
            l1 = new ArrayList();
            l2 = new ArrayList();
        }
        for(final Pair pair:input)
        {
            l1.add(pair.getValue0());
            l2.add(pair.getValue1());
        }

        return new Pair(Collections.unmodifiableList(l1),Collections.unmodifiableList(l2));
    }

    /**
     * The converse of the Convolution operator
     * See Zip
     * @param input sequence of triplets
     * @param  the type of the first element in the triplet
     * @param  the type of the second element in the triplet
     * @param  the type of the third element in the triplet
     * @throws java.lang.IllegalArgumentException if the input sequence is null
     * @return triplet of lists; the first element from each of the output sequences is the first triplet in the input sequence and so on,
     *          in order.
     */
    public static final Triplet,List,List> unzip3(final Iterable> input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.unzip(Iterable>): input is null");

        final List l1;
        final List l2;
        final List l3;
        if(input instanceof Collection) {
            final int size = ((Collection) input).size();
            l1 = new ArrayList(size);
            l2 = new ArrayList(size);
            l3 = new ArrayList(size);
        }
        else {
            l1 = new ArrayList();
            l2 = new ArrayList();
            l3 = new ArrayList();
        }

        for(final Triplet triplet:input)
        {
            l1.add(triplet.getValue0());
            l2.add(triplet.getValue1());
            l3.add(triplet.getValue2());
        }

        return new Triplet(Collections.unmodifiableList(l1),Collections.unmodifiableList(l2),Collections.unmodifiableList(l3));
    }

    /**
     * See Map
     * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into a sequence of output elements.
     * These sequences are concatenated into one final output sequence at the end of the transformation.
     * map: (T -> U list) -> T list -> U list
     * @param f a transformation function which takes a object of type T and returns a sequence of objects, presumably related, of type U
     * @param input a sequence to be fed into f
     * @param  the type of the element in the input sequence
     * @param  the type of the element in the output sequence
     * @return a list of type U containing the concatenated sequences of transformed values.
     */
    public static final List collect(final Func> f, final Iterable input)
    {
        List output = input instanceof Collection ? new ArrayList(((Collection) input).size()) : new ArrayList();
        for(final T element : input)
            output = Functional.concat(output, Functional.toList(f.apply(element)));
        return Collections.unmodifiableList(output);
    }

    /**
     * See Map
     * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into a sequence of output elements.
     * These sequences are concatenated into one final output sequence at the end of the transformation.
     * map: (T -> U list) -> T list -> U list
     * This is a curried implementation of 'collect'
     * @param f a transformation function which takes a object of type T and returns a sequence of objects, presumably related, of type U
     * @param  the type of the element in the input sequence
     * @param  the type of the element in the output sequence
     * @return a list of type U containing the concatenated sequences of transformed values.
     * @see Currying
     */
    public static final Func,List> collect(final Func> f)
    {
        return new Func, List>() {
            @Override
            public List apply(final Iterable input) {
                return Functional.collect(f,input);
            }
        };
    }

    /**
     * takeNAndYield: given an input sequence and an integer, return two sequences. The first output sequence returned is a list
     * containing the first 'howMany' elements of the 'input' sequence and the second output sequence contains all the remaining
     * elements of the 'input' sequence. The 'input' sequence is traversed only as far as is required to produce the first list
     * and so the remainder of the 'input' sequence remains unevaluated. If 'howMany' is greater than the number of elements in
     * 'input' then the output list will contain all the elements of the input and the output sequence will be empty.
     * This is like take but leaves the user with the ability to continue the traversal of the input sequence from the point
     * at which the 'take' stopped.
     * @param input the input sequence
     * @param howMany the number of elements to be included in the first output list
     * @param  the type of the element in the input sequence
     * @return a pair: (list, seq) - the list contains 'howMany' elements of 'input' and the sequence contains the remainder
     */
    public static final Pair,Iterable> takeNAndYield(final Iterable input, final int howMany)
    {
        if (input == null) throw new IllegalArgumentException("Functional.takeNAndYield: input is null");

        int counter = 0;
        final List output = new ArrayList(howMany);
        final Iterator position = input.iterator();
        if(howMany>0&&position.hasNext())
        {
            while(counter) new Iterable() {
                @Override
                public Iterator iterator() {
                    return position;
                }
            });
        }
        return Pair.with(output, input);
    }

    /**
     * append: given the input sequence and an item, return a new, lazily-evaluated sequence containing the input with the item
     * as the final element.
     * @param t the item to be appended
     * @param input the input sequence
     * @param  the type of the element in the input sequence
     * @return a sequence containing all the elements of 'input' followed by 't'
     * @see Lazy evaluation
     */
    public static final Iterable append(final T t, final Iterable input)
    {
        return new Iterable(){
            @Override
            public Iterator iterator() {
                return new Iterator(){
                    private int counter=0;
                    private Iterator iterator=input.iterator();
                    @Override
                    public boolean hasNext() {
                        return counter==0||iterator.hasNext();
                    }

                    @Override
                    public T next() {
                        return counter++==0 ? t : iterator.next();
                    }

                    @Override
                    public void remove() {
                        throw new UnsupportedOperationException("Functional.append(T,Iterable): it is not possible to remove elements from this sequence");
                    }
                };
            }
        };
    }

    /**
     * groupBy: similar to {@link #partition(Func,Iterable)} in that the input is grouped according to a function. This is more general than
     * partition though as the output can be an arbitrary number of groups, up to and including one group per item in the
     * input data set. The 'keyFn' is the grouping operator and it is used to determine the key at which any given element from
     * the input data set should be added to the output dictionary / map.
     * @param keyFn the grouping function. Given an element return the key to be used when storing this element in the dictionary
     * @param input the input sequence
     * @param  the type of the element in the input sequence
     * @param  the type of the element in the key
     * @return a java.util.Map containing a list of elements for each key
     */
    public static final Map> groupBy(final Func keyFn, final Iterable input)
    {
        if (keyFn == null) throw new IllegalArgumentException("Functional.groupBy(Func,Iterable): keyFn is null");
        if (input == null) throw new IllegalArgumentException("Functional.groupBy(Func,Iterable): input is null");

        final Map> intermediateResults = new HashMap>();
        for(final T element : input)
        {
            final U key = keyFn.apply(element);
            if(intermediateResults.containsKey(key))
                intermediateResults.get(key).add(element);
            else
            {
                final List list = new ArrayList();
                list.add(element);
                intermediateResults.put(key, list);
            }
        }
        final Map> output = new HashMap>(intermediateResults.size());
        for(final Map.Entry> entry : intermediateResults.entrySet())
             output.put(entry.getKey(),Collections.unmodifiableList(entry.getValue()));
        return Collections.unmodifiableMap(output);
    }

    /**
     * The Range class holds an inclusive lower bound and an exclusive upper bound. That is lower <= pos < upper
     */
    public static class Range
    {
        private final T lowerBound;
        private final T upperExBound;

        /**
         * Create a new Range object
         * @param lower the inclusive lower bound of the Range
         * @param upperEx the exclusive upper bound of the Range
         */
        public Range(final T lower, final T upperEx)
        {
            this.lowerBound=lower;
            this.upperExBound=upperEx;
        }

        /**
         * Return the inclusive lower bound
         * @return the inclusive lower bound
         */
        public T from(){return lowerBound;}

        /**
         * return the exclusive upper bound
         * @return the exclusive upper bound
         */
        public T to(){return upperExBound;}

        public boolean equals(final Object other)
        {
            if(! (other instanceof Range)) return false;
            final Range otherRange = (Range)other;
            return from().equals(otherRange.from()) && to().equals(otherRange.to());
        }

        public int hashCode()
        {
            return 13 * from().hashCode() + 7 * to().hashCode();
        }
    }

    /**
     * This list generator returns a list of Range objects which split the interval [1-'howManyElements') into 'howManyPartitions' Range objects.
     * If the interval cannot be divided exactly then the remainder is allocated evenly across the first
     * 'howManyElements' % 'howManyPartitions' Range objects.
     * @param howManyElements defines the exclusive upper bound of the interval to be split
     * @param howManyPartitions defines the number of Range objects to generate to cover the interval
     * @return a list of Range objects
     */
    public static List> partition(final int howManyElements, final int howManyPartitions)
    {
        return partition(Functional.range(0), howManyElements, howManyPartitions);
    }

    /**
     * This sequence generator returns a sequence of Range objects which split the interval [1-'howManyElements') into 'howManyPartitions'
     * Range objects. If the interval cannot be divided exactly then the remainder is allocated evenly across the first
     * 'howManyElements' % 'howManyPartitions' Range objects.
     * @param generator a function which generates members of the input sequence
     * @param howManyElements defines the exclusive upper bound of the interval to be split
     * @param howManyPartitions defines the number of Range objects to generate to cover the interval
     * @return a list of Range objects
     */
    public static List> partition(final Func generator, final int howManyElements, final int howManyPartitions)
    {
        if(howManyElements<=0) throw new IllegalArgumentException("Functional.partition() howManyElements cannot be non-positive");
        if(howManyPartitions<=0) throw new IllegalArgumentException("Functional.partition() howManyPartitions cannot be non-positive");

        final int size = howManyElements/howManyPartitions;
        final int remainder = howManyElements % howManyPartitions;

        assert size*howManyPartitions + remainder == howManyElements;

        final Integer seed = 0;
        final Func> boundsCalculator = new Func>() {
            @Override
            public Pair apply(final Integer integer) {
                return Pair.with(
                        generator.apply(1 + (integer * size + (integer <= remainder ? integer : remainder))),
                        integer+1);
            }
        };
        final Func finished = new Func() {
            @Override
            public Boolean apply(Integer integer) {
                return integer>howManyPartitions;
            }
        };

        final Iterable output = Functional.seq.unfold(boundsCalculator,finished,seed);

        final Iterator iterator = output.iterator();
        if(iterator==null || !iterator.hasNext()) throw new IllegalStateException("Somehow we have no entries in our sequence of bounds");
        T last = iterator.next();
        final List> retval = new ArrayList>(howManyPartitions);
        for(int i=0;i>() {
//            @Override
//            public Range apply(final Integer integer) {
//// inefficient - the upper bound is computed twice (once at the end of an iteration and once at the beginning of the next iteration)
//                return new Range( // 1 + the value because the init function expects the control range to start from one.
//                        generator.apply(1 + ((integer - 1) * size + (integer <= remainder + 1 ? integer - 1 : remainder))),
//                        generator.apply(1 + (integer * size + (integer <= remainder ? integer : remainder))));
//            }
//        }, howManyPartitions);
    }

    /**
     * Lazily-evaluated implementations of various of the algorithms
     * @see Lazy evaluation
     */
    public static final class seq
    {
        /**
         * See Map
         * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into an element in the output sequence.
         * map: (T -> U) -> T seq -> U seq
         * @param f a transformation function which takes a object of type A and returns an object, presumably related, of type B
         * @param input a sequence to be fed into f
         * @param  the type of the element in the input sequence
         * @param  the type of the element in the output sequence
         * @return a lazily-evaluated sequence of type B containing the transformed values.
         * @see Lazy evaluation
         */
        public static final Iterable map(final Func f, final Iterable input)
        {
            if(f==null) throw new IllegalArgumentException("f");
            if (input == null) throw new IllegalArgumentException("input");

            return new Iterable() {
                @Override
                public final Iterator iterator() {
                    return new Iterator() {
                        private final Iterator _input=input.iterator();
                        private final Func _f = f;
                        @Override
                        public final boolean hasNext() {
                            return _input.hasNext();
                        }

                        @Override
                        public final U next() {
                            return _f.apply(_input.next());
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.map(Func,Iterable): Removing elements is strictly prohibited");
                        }
                    };
                }
            };
        }

        /**
         * See Map
         * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into an element in the output sequence.
         * map: (T -> U) -> T seq -> U seq
         * @param f a transformation function which takes a object of type A and returns an object, presumably related, of type B
         * @param  the type of the element in the input sequence
         * @param  the type of the element in the output sequence
         * @return a curried function that expects an input sequence which it feeds to the transformation f which returns a lazily-evaluated
         * sequence of type U containing the transformed values.
         * @see Currying
         * @see Lazy evaluation
         */
        public static final Func,Iterable> map(final Func f)
        {
            return new Func, Iterable>() {
                @Override
                public Iterable apply(final Iterable input) {
                    return Functional.seq.map(f, input);
                }
            };
        }

        /**
         * See Map
         * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into an element in the output sequence.
         * mapi: (Integer -> T -> U) -> T seq -> U seq
         * @param f a transformation function which takes a object of type A and returns an object, presumably related, of type B
         * @param input a sequence to be fed into f
         * @param  the type of the element in the input sequence
         * @param  the type of the element in the output sequence
         * @return a lazily-evaluated sequence of type B containing the transformed values.
         * @see Lazy evaluation
         */
        public static final Iterable mapi(final Func2 f, final Iterable input)
        {
            if(f==null) throw new IllegalArgumentException("f");
            if (input == null) throw new IllegalArgumentException("input");

            return new Iterable() {
                @Override
                public final Iterator iterator() {
                    return new Iterator() {
                        private final Iterator _input=input.iterator();
                        private final Func2 _f = f;
                        private int counter = 0;
                        @Override
                        public final boolean hasNext() {
                            return _input.hasNext();
                        }

                        @Override
                        public final U next() {
                            return _f.apply(counter++,_input.next());
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.map(Func,Iterable): Removing elements is strictly prohibited");
                        }
                    };
                }
            };
        }

        /**
         * See Map
         * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into an element in the output sequence.
         * mapi: (int -> T -> U) -> T seq -> U seq
         * @param f a transformation function which takes a object of type A and returns an object, presumably related, of type B
         * @param  the type of the element in the input sequence
         * @param  the type of the element in the output sequence
         * @return a curried function that expects an input sequence which it feeds to the transformation f which returns a lazily-evaluated
         * sequence of type U containing the transformed values.
         * @see Currying
         * @see Lazy evaluation
         */
        public static final Func,Iterable> mapi(final Func2 f)
        {
            return new Func, Iterable>() {
                @Override
                public Iterable apply(final Iterable input) {
                    return Functional.seq.mapi(f, input);
                }
            };
        }

        /**
         * Concatenate two sequences and return a new sequence containing the concatenation.
         * @param list1 first input sequence
         * @param list2 second input sequence
         * @param  the type of the element in the input sequence
         * @return a lazily-evaluated sequence containing the elements of the first sequence followed by the elements of the second sequence
         * @see Lazy evaluation
         */
        public static final Iterable concat(final Iterable list1, final Iterable list2)
        {
            if(list1==null) throw new IllegalArgumentException("Functional.seq.concat(Iterable,Iterable): list1 is null");
            if(list2==null) throw new IllegalArgumentException("Functional.seq.concat(Iterable,Iterable): list2 is null");

            return new Iterable()
            {
                public Iterator iterator()
                {
                    return new Iterator() {
                        private final Iterator _s1 = list1.iterator();
                        private final Iterator _s2 = list2.iterator();
                        @Override
                        public boolean hasNext() {
                            return _s1.hasNext() || _s2.hasNext();
                        }

                        @Override
                        public T next() {
                            return _s1.hasNext() ? _s1.next() : _s2.next();
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.concat(Iterable,Iterable): remove is not supported");
                        }
                    };
                }
            };
        }

        /**
         * See Filter
         * @param f a filter function. This is passed each input element in turn and returns either true or false. If true then
         *             the input element is passed through to the output otherwise it is ignored.
         * @param input a sequence of objects
         * @param  the type of the element in the input sequence
         * @return a lazily-evaluated sequence which contains zero or more of the elements of the input sequence. Each element is included only if
         * the filter function returns true for the element.
         * @see Lazy evaluation
         */
        public static final Iterable filter(final Func f, final Iterable input) //throws NoSuchElementException, IllegalArgumentException, UnsupportedOperationException
        {
            if(f==null) throw new IllegalArgumentException("f");
            if (input == null) throw new IllegalArgumentException("input");

            return new Iterable() {
                @Override
                public final Iterator iterator() {
                    return new Iterator() {
                        private final Iterator _input=input.iterator();
                        private final Func _f = f;
                        private T _next = null;
                        @Override
                        public final boolean hasNext() {
                            while(_next==null && // ie we haven't already read the next element
                                _input.hasNext())
                            {
                                final T next = _input.next();
                                if(_f.apply(next))
                                {
                                    _next=next;
                                    return true;
                                }
                            }
                            return _next!=null;
                        }

                        @Override
                        public final T next() {
                            if(hasNext())
                            {
                                final T next = _next;
                                _next=null;
                                return next;
                            }
                            throw new java.util.NoSuchElementException();
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.filter(Func,Iterable): Removing elements is strictly prohibited");
                        }
                    };
                }
            };
        }

        /**
         * See Filter
         * @param f a filter function. This is passed each input element in turn and returns either true or false. If true then
         *             the input element is passed through to the output otherwise it is ignored.
         * @param  the type of the element in the input sequence
         * @return a lazily-evaluated sequence which contains zero or more of the elements of the input sequence. Each element is included only if
         * the filter function returns true for the element.
         * @see Lazy evaluation
         */
        public static final Func,Iterable> filter(final Func f)
        {
            return new Func,Iterable>(){
                @Override
                public Iterable apply(final Iterable input) {
                    return Functional.seq.filter(f,input);
                }
            };
        }

        /**
         * choose: this is a map transformation with the difference being that the number of elements in the output sequence may
         * be between zero and the number of elements in the input sequence.
         * See Map
         * choose: (A -> B option) -> A list -> B list
         * @param f map function. This transforms the input element into an Option
         * @param input input sequence
         * @param  the type of the element in the input sequence
         * @param  the type of the element in the output sequence
         * @return a lazily-evaluated sequence of transformed elements, numbering less than or equal to the number of input elements
         * @see Lazy evaluation
         */
        public static final Iterable choose(final Func> f, final Iterable input)
        {
            if(f==null) throw new IllegalArgumentException("f");
            if (input == null) throw new IllegalArgumentException("input");

            return new Iterable() {
                @Override
                public final Iterator iterator() {
                    return new Iterator() {
                        private final Iterator _input=input.iterator();
                        private final Func> _f = f;
                        private Option _next = Option.None();
                        @Override
                        public final boolean hasNext() {
                            while(_next.isNone() && // ie we haven't already read the next element
                                    _input.hasNext())
                            {
                                final Option next = _f.apply(_input.next());
                                if(next.isSome())
                                {
                                    _next=next;
                                    return true;
                                }
                            }
                            return _next.isSome();
                        }

                        @Override
                        public final U next()
                        {
                            if(hasNext())
                            {
                                final Option next = _next;
                                _next=Option.None();
                                try {
                                    return next.Some();
                                } catch(final OptionNoValueAccessException e) { throw new java.util.NoSuchElementException(); }
                            }
                            throw new java.util.NoSuchElementException();
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.choose(Func,Iterable): Removing elements is strictly prohibited");
                        }
                    };
                }
            };
        }

        /**
         * choose: this is a map transformation with the difference being that the number of elements in the output sequence may
         * be between zero and the number of elements in the input sequence.
         * See Map
         * choose: (A -> B option) -> A list -> B list
         * @param f map function. This transforms the input element into an Option
         * @param  the type of the element in the input sequence
         * @param  the type of the element in the output sequence
         * @return a lazily-evaluated sequence of transformed elements, numbering less than or equal to the number of input elements
         * @see Lazy evaluation
         */
        public static final Func,Iterable> choose(final Func> f)
        {
            return new Func, Iterable>() {
                @Override
                public Iterable apply(final Iterable input) {
                    return Functional.seq.choose(f,input);
                }
            };
        }

        /**
         * The init function, not dissimilar to list comprehensions, which is used to return a new finite sequence whose contents are
         * determined by successive calls to the function f.
         * init: (int -> T) -> int -> T seq
         * @param f generator function used to produce the individual elements of the output sequence.
         *          This function is called by init with the unity-based position of the current element in the output sequence being
         *          produced. Therefore, the first time f is called it will receive a literal '1' as its argument; the second time
         *          '2'; etc.
         * @param howMany the number of elements in the output sequence
         * @param  the type of the element in the output sequence
         * @return a lazily-evaluated sequence which will contain no more than 'howMany' elements of type 'T' which were generated by the function 'f'
         * @see Lazy evaluation
         */
        public final static Iterable init(final Func f,final int howMany)
        {
            if(f==null) throw new IllegalArgumentException("f");
            if(howMany<1) throw new IllegalArgumentException("howMany");

            return new Iterable()
            {
                @Override
                public Iterator iterator() {
                    return new Iterator()
                    {
                        private int _counter=1;
                        private final Func _f = f;
                        @Override
                        public boolean hasNext() {
                            return _counter<=howMany;
                        }

                        @Override
                        public T next() {
                            return _f.apply(_counter++);
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.init(Func,Iterable): Removing elements is strictly prohibited");
                        }
                    };
                }
            };
        }

        /**
         * The init function, not dissimilar to list comprehensions, which is used to return a new infinite sequence whose contents are
         * determined by successive calls to the function f.
         * init: (int -> T) -> T seq
         * @param f generator function used to produce the individual elements of the output sequence.
         *          This function is called by init with the unity-based position of the current element in the output sequence being
         *          produced. Therefore, the first time f is called it will receive a literal '1' as its argument; the second time
         *          '2'; etc.
         * @param  the type of the element in the output sequence
         * @return a potentially infinite sequence containing elements of type 'T' which were generated by the function 'f'
         * @see Lazy evaluation
         */
        public final static Iterable init(final Func f)
        {
            if(f==null) throw new IllegalArgumentException("f");

            return new Iterable()
            {
                @Override
                public Iterator iterator() {
                    return new Iterator()
                    {
                        private int _counter=1;
                        private final Func _f = f;
                        @Override
                        public boolean hasNext() {
                            return true;
                        }

                        @Override
                        public T next() {
                            return _f.apply(_counter++);
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.init(Func,Iterable): Removing elements is strictly prohibited");
                        }
                    };
                }
            };
        }

        /**
         * See Map
         * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into a sequence of output elements.
         * These sequences are concatenated into one final output sequence at the end of the transformation.
         * map: (T -> U list) -> T list -> U list
         * @param f a transformation function which takes a object of type T and returns a sequence of objects, presumably related, of type U
         * @param input a sequence to be fed into f
         * @param  the type of the element in the input sequence
         * @param  the type of the element in the output sequence
         * @return a lazily-evaluated sequence of type U containing the concatenated sequences of transformed values.
         * @see Lazy evaluation
         */
        public static final Iterable collect(final Func> f, final Iterable input)
        {
            if(f==null) throw new IllegalArgumentException("Functional.seq.collect: f is null");
            if(input==null) throw new IllegalArgumentException("Functional.seq.collect: input is null");

            return new Iterable(){

                @Override
                public Iterator iterator() {
                    return new Iterator(){
                        private final Iterator it = input.iterator();
                        private List cache = new ArrayList();
                        private Iterator cacheIterator = cache.iterator();
                        @Override
                        public boolean hasNext() {
                            return it.hasNext() || cacheIterator.hasNext();
                        }

                        @Override
                        public U next() {
                            if(cacheIterator.hasNext()) return cacheIterator.next();
                            cache = toList(f.apply(it.next()));
                            cacheIterator=cache.iterator();
                            return cacheIterator.next();
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.collect: remove is not supported");
                        }
                    };
                }
            };
        }

        /**
         * See Map
         * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into a sequence of output elements.
         * These sequences are concatenated into one final output sequence at the end of the transformation.
         * map: (T -> U list) -> T list -> U list
         * @param f a transformation function which takes a object of type T and returns a sequence of objects, presumably related, of type U
         * @param  the type of the element in the input sequence
         * @param  the type of the element in the output sequence
         * @return a function returning a lazily-evaluated sequence of type U containing the concatenated sequences of transformed values.
         * @see Lazy evaluation
         */
        public static final Func,Iterable> collect(final Func> f)
        {
            return new Func, Iterable>() {
                @Override
                public Iterable apply(final Iterable input) {
                    return Functional.seq.collect(f,input);
                }
            };
        }

        /**
         * skip: the converse of take. Given a list return another list containing those elements that follow the
         * first 'howMany' elements. That is, if we skip(1,[1,2,3]) then we have [2,3]
         * @param howMany a non-negative number of elements to be discarded from the input sequence
         * @param input the input sequence
         * @param  the type of the element in the input sequence
         * @return a lazily-evaluated sequence containing the remaining elements after the first 'howMany' elements of 'list' or an empty list if more elements
         * are skipped than are present in the 'list'
         * @see Lazy evaluation
         */
        public static final Iterable skip(final int howMany, final Iterable input) {
            if (howMany < 0)
                throw new IllegalArgumentException("Functional.skip(int,Iterable): howMany is negative");
            if (input == null) throw new IllegalArgumentException("Functional.skip(int,Iterable): input is null");

            return new Iterable() {
                @Override
                public Iterator iterator() {
                    return new Iterator() {
                        private final Iterator it = input.iterator();
                        private boolean haveWeSkipped = false;

                        @Override
                        public boolean hasNext() {
                            if (haveWeSkipped && it.hasNext()) return true;
                            if (haveWeSkipped) return false;
                            for (int i = 0; i < howMany; ++i)
                                if (it.hasNext()) it.next();
                                else return false;
                            haveWeSkipped = true;
                            return it.hasNext();
                        }

                        @Override
                        public T next() {
                            final boolean another = hasNext();
                            return it.next();
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.skip: remove is not supported");
                        }
                    };
                }
            };
        }

        /**
         * skip: the converse of take. Given a list return another list containing those elements that follow the
         * first 'howMany' elements. That is, if we skip(1,[1,2,3]) then we have [2,3]
         * @param howMany a non-negative number of elements to be discarded from the input sequence
         * @param  the type of the element in the input sequence
         * @return a lazily-evaluated sequence containing the remaining elements after the first 'howMany' elements of 'list' or an empty list if more elements
         * are skipped than are present in the 'list'
         * @see Lazy evaluation
         */
        public static final Func,Iterable> skip(final int howMany)
        {
            return new Func, Iterable>() {
                @Override
                public Iterable apply(final Iterable input) {
                    return Functional.seq.skip(howMany, input);
                }
            };
        }

        /**
         * take: given a sequence return another sequence containing the first 'howMany' elements
         * @param howMany a positive number of elements to be returned from the input sequence
         * @param list the input sequence
         * @param  the type of the element in the input sequence
         * @return a sequence containing the first 'howMany' elements of 'list'
         * @throws java.util.NoSuchElementException if more elements are requested than are present in the input sequence
         */
        public static finalIterable take(final int howMany, final Iterable list)
        {
            if(howMany<0) throw new IllegalArgumentException("Functional.seq.take(int,Iterable): howMany is negative");
            if(list==null) throw new IllegalArgumentException("Functional.seq.take(int,Iterable): list is null");

            if(howMany==0) return new ArrayList(0);

            return new Iterable() {
                @Override
                public Iterator iterator() {
                    return new Iterator(){
                        private final Iterator it = list.iterator();
                        private int howManyHaveWeRetrievedAlready = 0;
                        @Override
                        public boolean hasNext() {
                            return howManyHaveWeRetrievedAlready=howMany)
                                throw new java.util.NoSuchElementException("Cannot request additional elements from input");
                            final T next = it.next();
                            howManyHaveWeRetrievedAlready++;
                            return next;
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.take: remove is not supported");
                        }
                    };
                }
            };
        }

        /**
         * take: given a sequence return another sequence containing the first 'howMany' elements
         * @param howMany a positive number of elements to be returned from the input sequence
         * @param  the type of the element in the input sequence
         * @return a sequence containing the first 'howMany' elements of 'list'
         * @throws java.util.NoSuchElementException if more elements are requested than are present in the input sequence
         */
        public static final Func,Iterable> take(final int howMany)
        {
            return new Func, Iterable>() {
                @Override
                public Iterable apply(final Iterable input) {
                    return Functional.seq.take(howMany, input);
                }
            };
        }

        /**
         * See Unfold and
         * Anamorphism
         * This is the converse of fold
         * unfold: (b -> (a, b)) -> (b -> Bool) -> b -> [a]
         */
        public final static Iterable unfold(final Func> unspool, final Func finished, final B seed)
        {
            if(unspool==null) throw new IllegalArgumentException("unspool");
            if(finished==null) throw new IllegalArgumentException("finished");

            return new Iterable() {
                @Override
                public Iterator iterator() {
                    return new Iterator() {
                        B next = seed;
                        @Override
                        public boolean hasNext() {
                            return !finished.apply(next);
                        }

                        @Override
                        public A next() {
                            final Pair t = unspool.apply(next);
                            next = t.getValue1();
                            return t.getValue0();
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.unfold(Func,Func,B): it is not possible to remove elements from this sequence");
                        }
                    };
                }
            };
        }

        /**
         * See Unfold
         * and Anamorphism
         * This is the converse of fold
         * unfold: (b -> (a, b)) -> (b -> Bool) -> b -> [a]
         */
        public final static Iterable unfold(final Func>> unspool, final B seed)
        {
            if(unspool==null) throw new IllegalArgumentException("unspool");

            return new Iterable() {
                @Override
                public Iterator iterator() {
                    return new Iterator() {
                        B next = seed;
                        Option> temp;
                        @Override
                        public boolean hasNext() {
                            temp = unspool.apply(next);
                            return temp.isSome();
                        }

                        @Override
                        public A next() {
                            next = temp.Some().getValue1();
                            return temp.Some().getValue0();
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.unfold(Func,B): it is not possible to remove elements from this sequence");
                        }
                    };
                }
            };
        }

        /**
         * This sequence generator returns a list of Range objects which split the interval [1-'howManyElements') into 'howManyPartitions' Range objects.
         * If the interval cannot be divided exactly then the remainder is allocated evenly across the first
         * 'howManyElements' % 'howManyPartitions' Range objects.
         * @param howManyElements defines the exclusive upper bound of the interval to be split
         * @param howManyPartitions defines the number of Range objects to generate to cover the interval
         * @return a list of Range objects
         */
        public static Iterable> partition(final int howManyElements, final int howManyPartitions)
        {
            return partition(Functional.range(0), howManyElements, howManyPartitions);
        }

        /**
         * This sequence generator returns a sequence of Range objects which split the interval [1-'howManyElements') into 'howManyPartitions'
         * Range objects. If the interval cannot be divided exactly then the remainder is allocated evenly across the first
         * 'howManyElements' % 'howManyPartitions' Range objects.
         * @param generator a function which generates members of the input sequence
         * @param howManyElements defines the exclusive upper bound of the interval to be split
         * @param howManyPartitions defines the number of Range objects to generate to cover the interval
         * @return a list of Range objects
         */
        public static Iterable> partition(final Func generator, final int howManyElements, final int howManyPartitions)
        {
            if(howManyElements<=0) throw new IllegalArgumentException("Functional.partition() howManyElements cannot be non-positive");
            if(howManyPartitions<=0) throw new IllegalArgumentException("Functional.partition() howManyPartitions cannot be non-positive");

            final int size = howManyElements/howManyPartitions;
            final int remainder = howManyElements % howManyPartitions;

            assert size*howManyPartitions + remainder == howManyElements;

            final Integer seed = 0;
            final Func> boundsCalculator = new Func>() {
                @Override
                public Pair apply(final Integer integer) {
                    return Pair.with(
                            generator.apply(1 + (integer * size + (integer <= remainder ? integer : remainder))),
                            integer+1);
                }
            };
            final Func finished = new Func() {
                @Override
                public Boolean apply(Integer integer) {
                    return integer>howManyPartitions;
                }
            };

            final Iterable output = Functional.seq.unfold(boundsCalculator,finished,seed);

            return new Iterable>() {
                @Override
                public Iterator> iterator() {
                    return new Iterator>() {
                        final Iterator iterator = output.iterator();
                        T last = iterator.next();
                        @Override
                        public boolean hasNext() {
                            return iterator.hasNext();
                        }

                        @Override
                        public Range next() {
                            final T next = iterator.next();
                            final Range retval = new Range(last, next);
                            last = next;
                            return retval;
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.partition(Func,int,int): it is not possible to remove elements from this sequence");
                        }
                    };
                }
            };
        }

        /**
         * The Convolution operator
         * See Zip
         * @param l1 input sequence
         * @param l2 input sequence
         * @param  the type of the element in the first input sequence
         * @param  the type of the element in the second input sequence
         * @throws java.lang.IllegalArgumentException if either input sequence is null or if the sequences have differing lengths.
         * @return list of pairs; the first element from each of the two input sequences is the first pair in the output sequence and so on,
         *          in order. If the sequences do not have the same number of elements then an exception is thrown.
         */
        public static final Iterable> zip(final Iterable l1, final Iterable l2)
        {
            if(l1==null) throw new IllegalArgumentException("Functional.seq.zip(Iterable,Iterable): l1 is null");
            if(l2==null) throw new IllegalArgumentException("Functional.seq.zip(Iterable,Iterable): l2 is null");

            return new Iterable>() {
                @Override
                public Iterator> iterator() {
                    return new Iterator>() {
                        private final Iterator l1_it = l1.iterator();
                        private final Iterator l2_it = l2.iterator();
                        @Override
                        public boolean hasNext() {
                            final boolean l1_it_hasNext = l1_it.hasNext();
                            final boolean l2_it_hasNext = l2_it.hasNext();
                            if(l1_it_hasNext != l2_it_hasNext) throw new IllegalArgumentException("Functional.seq.zip(Iterable,Iterable): l1 and l2 have differing numbers of elements");
                            return l1_it_hasNext && l2_it_hasNext;
                        }

                        @Override
                        public Pair next() {
                            return Pair.with(l1_it.next(),l2_it.next());
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.zip(Iterable,Iterable): it is not possible to remove elements from this sequence");
                        }

//                        @Override
//                        public void forEachRemaining(Consumer> action) {
//
//                        }
                    };
                }
            };
        }

        public static final Func,Iterable>> zip(final Iterable l1)
        {
            return new Func,Iterable>>() {
                @Override
                public Iterable> apply(final Iterable l2) {
                    return Functional.seq.zip(l1, l2);
                }
            };
        }

        /**
         * The Convolution operator
         * See Zip
         * @param l1 input sequence
         * @param l2 input sequence
         * @param l3 input sequence
         * @param  the type of the element in the first input sequence
         * @param  the type of the element in the second input sequence
         * @param  the type of the element in the third input sequence
         * @throws java.lang.IllegalArgumentException if either input sequence is null or if the sequences have differing lengths.
         * @return list of pairs; the first element from each of the two input sequences is the first pair in the output sequence and so on,
         *          in order. If the sequences do not have the same number of elements then an exception is thrown.
         */
        public static final Iterable> zip3(final Iterable l1, final Iterable l2, final Iterable l3)
        {
            if(l1==null) throw new IllegalArgumentException("Functional.seq.zip3(Iterable,Iterable,Iterable): l1 is null");
            if(l2==null) throw new IllegalArgumentException("Functional.seq.zip3(Iterable,Iterable,Iterable): l2 is null");
            if(l3==null) throw new IllegalArgumentException("Functional.seq.zip3(Iterable,Iterable,Iterable): l3 is null");

            return new Iterable>() {
                @Override
                public Iterator> iterator() {
                    return new Iterator>() {
                        private final Iterator l1_it = l1.iterator();
                        private final Iterator l2_it = l2.iterator();
                        private final Iterator l3_it = l3.iterator();
                        @Override
                        public boolean hasNext() {
                            final boolean l1_it_hasNext = l1_it.hasNext();
                            final boolean l2_it_hasNext = l2_it.hasNext();
                            final boolean l3_it_hasNext = l3_it.hasNext();
                            if(l1_it_hasNext != l2_it_hasNext || l1_it_hasNext != l3_it_hasNext) throw new IllegalArgumentException("Functional.seq.zip3(Iterable,Iterable,Iterable): the input sequences have differing numbers of elements");
                            return l1_it_hasNext && l2_it_hasNext && l3_it_hasNext;
                        }

                        @Override
                        public Triplet next() {
                            return Triplet.with(l1_it.next(),l2_it.next(),l3_it.next());
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.zip3(Iterable,Iterable,Iterable): it is not possible to remove elements from this sequence");
                        }

//                        @Override
//                        public void forEachRemaining(Consumer> action) {
//
//                        }
                    };
                }
            };
        }

        public static final Func,Iterable>> zip3(final Iterable l1,final Iterable l2)
        {
            return new Func,Iterable>>() {
                @Override
                public Iterable> apply(final Iterable l3) {
                    return Functional.seq.zip3(l1, l2, l3);
                }
            };
        }
    }

    /**
     * See Recursion
     * Recursive implementations of (some of) the algorithms contained herein
     */
    public final static class rec
    {
        private static final Iterable filter(final Func f, final Iterator input, final List accumulator)
        {
            if(input.hasNext())
            {
                A next = input.next();
                if(f.apply(next)) accumulator.add(next);
                return filter(f,input,accumulator);
            }
            else return accumulator;
        }

        /**
         * See Filter
         * This is a recursive implementation of filter.
         * See Recursion
         * @param f a filter function. This is passed each input element in turn and returns either true or false. If true then
         *             the input element is passed through to the output otherwise it is ignored.
         * @param  the type of the element in the input sequence
         * @return a sequence which contains zero or more of the elements of the input sequence. Each element is included only if
         * the filter function returns true for the element.
         */
        public static final Iterable filter(final Func f, final Iterable input)
        {
            return filter(f,input.iterator(),input instanceof Collection ? new ArrayList(((Collection) input).size()) : new ArrayList());
        }

        private static final Iterable map(final Func f, final Iterator input, final List accumulator)
        {
            if(input.hasNext())
            {
                accumulator.add(f.apply(input.next()));
                return map(f,input,accumulator);
            }
            else return accumulator;
        }

        /**
         * See Map
         * This is a recursive implementation of the map function. It is a 1-to-1 transformation.
         * Every element in the input sequence will be transformed into an element in the output sequence.
         * map: (A -> B) -> A seq -> B seq
         * See Recursion
         * @param f a transformation function which takes a object of type A and returns an object, presumably related, of type B
         * @param input a sequence to be fed into f
         * @param  the type of the element in the input sequence
         * @param  the type of the element in the output sequence
         * @return a seq of type B containing the transformed values.
         */
        public static final Iterable map(final Func f, final Iterable input)
        {
            return map(f,input.iterator(),input instanceof Collection ? new ArrayList(((Collection) input).size()) : new ArrayList());
        }

        private final static A fold(final Func2 f, final A initialValue, final Iterator input)
        {
            if(input.hasNext())
            {
                B next = input.next();
                return fold(f,f.apply(initialValue,next),input);
            }
            else return initialValue;
        }

        /**
         * See Fold
         * fold: (A -> B -> A) -> A -> B list -> A
         * This is a recursive implementation of fold
         * See Recursion
         * @param f the aggregation function
         * @param initialValue the seed for the aggregation
         * @param  the type of the initialValue / seed
         * @param  the type of the element in the output sequence
         * @return the aggregated value
         */
        public final static A fold(final Func2 f, final A initialValue, final Iterable input)
        {
            return fold(f,initialValue,input.iterator());
        }

        private final static List unfold(final Func> unspool, final Func finished, final B seed, final List accumulator)
        {
            if(finished.apply(seed)) return accumulator;
            final Pair p = unspool.apply(seed);
            accumulator.add(p.getValue0());
            return unfold(unspool,finished,p.getValue1(),accumulator);
        }

        /**
         * See Unfold
         * and Anamorphism
         * unfold: (b -> (a, b)) -> (b -> Bool) -> b -> [a]
         * This is a recursive implementation of unfold
         * See Recursion
         */
        public final static List unfold(final Func> unspool, final Func finished, final B seed)
        {
            return unfold(unspool,finished,seed,new ArrayList());
        }

        private final static List unfold(final Func>> unspool, final B seed, final List accumulator)
        {
            final Option> p = unspool.apply(seed);
            if(p.isNone()) return accumulator;
            accumulator.add(p.Some().getValue0());
            return unfold(unspool,p.Some().getValue1(),accumulator);
        }

        /**
         * See Unfold
         * and Anamorphism
         * unfold: (b -> (a, b)) -> (b -> Bool) -> b -> [a]
         * This is a recursive implementation of unfold
         * See Recursion
         */
        public final static List unfold(final Func>> unspool, final B seed)
        {
            return unfold(unspool,seed,new ArrayList());
        }
    }
        /*
        // Following are functions for non-list collections
        */

    public static final Map map_dict(final Func> f, final Iterable input)
    {
        final Map results = new HashMap();
        for (final A a : input)
        {
            final Map.Entry intermediate = f.apply(a);
            results.put(intermediate.getKey(), intermediate.getValue());
        }
        return results;
    }

    /**
     * Implementations of the algorithms contained herein which return sets
     * See Set
     */
    public static final class set
    {
        /**
         * See Filter
         * @param pred a filter function. This is passed each input element in turn and returns either true or false. If true then
         *             the input element is passed through to the output otherwise it is ignored.
         * @param input a sequence of objects
         * @param  the type of the element in the input sequence
         * @return a set which contains zero or more of the elements of the input sequence. Each element is included only if the filter
         *          function returns true for the element.
         */
        public final static Set filter(final Func pred, final Iterable input)
        {
            final Set output = input instanceof Collection ? new HashSet(((Collection) input).size()) : new HashSet();
            for(final A element : input)
            {
                if(pred.apply(element))
                    output.add(element);
            }
            return Collections.unmodifiableSet(output);
        }

        /**
         * See Map
         * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into a sequence of output elements.
         * These sequences are concatenated into one final output sequence at the end of the transformation.
         * map: (T -> U list) -> T list -> U list
         * @param f a transformation function which takes a object of type T and returns a sequence of objects, presumably related, of type U
         * @param input a sequence to be fed into f
         * @param  the type of the element in the input sequence
         * @param  the type of the element in the output sequence
         * @return a set of type U containing the concatenated sequences of transformed values.
         */
        public static final Set collect(final Func> f, final Iterable input)
        {
            Set output = input instanceof Collection ? new HashSet(((Collection) input).size()) : new HashSet();
            for(final T element : input)
                output.addAll(Functional.toSet(f.apply(element)));
            return Collections.unmodifiableSet(output);
        }

        /**
         * See Map
         * This implementation of the map function returns a set instead of an ordered sequence. It is a 1-to-1 transformation.
         * Every element in the input sequence will be transformed into an element in the output sequence.
         * map: (A -> B) -> A seq -> B set
         * @param f a transformation function which takes a object of type A and returns an object, presumably related, of type B
         * @param input a sequence to be fed into f
         * @param  the type of the element in the input sequence
         * @param  the type of the element in the output sequence
         * @return a set of type B containing the transformed values.
         * @throws UnsupportedOperationException if the add operation
         *         is not supported by this set
         * @throws ClassCastException if the class of the specified element
         *         prevents it from being added to this set
         * @throws NullPointerException if the specified element is null and this
         *         set does not permit null elements
         * @throws IllegalArgumentException if some property of the specified element
         *         prevents it from being added to this set
         */
        public final static  Set map(final Func f, final Iterable input)
        {
            final Set output = input instanceof Collection ? new HashSet(((Collection) input).size()) : new HashSet();
            for(final A a : input)
                output.add(f.apply(a));
            return Collections.unmodifiableSet(output);
        }

        /**
         * Concatenate two sequences and return a new list containing the concatenation.
         * @param list1 first input sequence
         * @param list2 second input sequence
         * @param  the type of the element in the input sequence
         * @return a set containing the elements of the first sequence and the elements of the second sequence
         */
        public static final Set concat(final Set list1, final Set list2)
        {
            if(list1==null) throw new IllegalArgumentException("Functional.concat(Set,List): list1 is null");
            if(list2==null) throw new IllegalArgumentException("Functional.concat(Set,List): list2 is null");

            if(list1.size()==0) return Collections.unmodifiableSet(list2);
            if(list2.size()==0) return Collections.unmodifiableSet(list1);

            final Set newList = new HashSet(list1);
            final boolean didItChange = newList.addAll(list2);
            return Collections.unmodifiableSet(newList);
        }

        /*
        * Non-destructive wrappers for set intersection and set difference
         */

        /**
         * Non-destructive wrapper for set intersection
         * @param e1 input set
         * @param e2 input set
         * @param 
         * @return a set containing those elements which are contained within both sets 'e1' and 'e2'
         */
        public final static Set intersection(final Set e1, final Set e2)
        {
            Set i = new HashSet(e1);
            i.retainAll(e2);
            return Collections.unmodifiableSet(i);
        }

        /**
         * Non-destructive wrapper for set difference
         * @param inSet input set
         * @param notInSet input set
         * @param 
         * @return a set of those elements which are in 'inSet' and not in 'notInSet'
         */
        public final static Set asymmetricDifference(final Set inSet, final Set notInSet)
        {
            Set i = new HashSet(inSet);
            i.removeAll(notInSet);
            return Collections.unmodifiableSet(i);
        }
    }

    /**
     * Implementations of the algorithms contained herein in terms of 'fold'
     * See Fold
     */
    public static final class inTermsOfFold
    {
        /**
         * See Map
         * This is a 1-to-1 transformation. Every element in the input sequence will be transformed into an element in the output
         * sequence.
         * map: (A -> B) -> A seq -> B list
         * @param f a transformation function which takes a object of type A and returns an object, presumably related, of type B
         * @param l a sequence to be fed into f
         * @param  the type of the element in the input sequence
         * @param  the type of the element in the output sequence
         * @return a list of type B containing the transformed values.
         * @see Fold
         */
        public static final List map(final Func f, final Iterable l)
        {
            final List l2 = Functional.fold(new Func2,U,List>() {
                @Override
                public List apply(final List state, final U o2) {
                    state.add(f.apply(o2));
                    return state;
                }
            }, l instanceof Collection ? new ArrayList(((Collection) l).size()) : new ArrayList(), l);
            return l2;
        }

        /**
         * See Filter
         * @param predicate a filter function. This is passed each input element in turn and returns either true or false. If true then
         *             the input element is passed through to the output otherwise it is ignored.
         * @param l a sequence of objects
         * @param  the type of the element in the input sequence
         * @return a list which contains zero or more of the elements of the input sequence. Each element is included only if the filter
         *          function returns true for the element.
         * @see Fold
         */
        public static final List filter(final Func predicate, final Iterable l)
        {
            final List l2 = Functional.fold(new Func2, T, List>() {
                @Override
                public List apply(final List ts, final T o) {
                    if(predicate.apply(o)) ts.add(o);
                    return ts;
                }
            }, l instanceof Collection ? new ArrayList(((Collection) l).size()) : new ArrayList(), l);
            return l2;
        }

        /**
         * The init function, not dissimilar to list comprehensions, which is used to return a new finite list whose contents are
         * determined by successive calls to the function f.
         * init: (int -> A) -> int -> A list
         * @param f generator function used to produce the individual elements of the output list. This function is called by init
         *          with the unity-based position of the current element in the output list being produced. Therefore, the first time
         *          f is called it will receive a literal '1' as its argument; the second time '2'; etc.
         * @param howMany the number of elements in the output list
         * @param  the type of the element in the output sequence
         * @return a list of 'howMany' elements of type 'T' which were generated by the function 'f'
         * @see Fold
         */
        public static final List init(final Func f, final int howMany)
        {
            return Functional.unfold(new Func>>() {
                @Override
                public Option> apply(final Integer a) {
                    return a<=howMany ? Option.toOption(Pair.with(f.apply(a), a + 1)) : Option.>None();
                }
            }, new Integer(1));
        }
    }

    /**
     * This class provides alternative implementations of those standard functions which would ordinarily throw an exception
     * in the event of an unexpected failure. The functions in this class will indicate the failure in a different manner, typically
     * using the Option type.
     */
    public final static class noException
    {
        /**
         * Find the first element from the input sequence for which the supplied predicate returns true
         * find: (A -> bool) -> A list -> A option
         * @param f predicate
         * @param input sequence
         * @param  the type of the element in the input sequence
         * @throws java.lang.IllegalArgumentException if f or input are null
         * @return the first element from the input sequence for which the supplied predicate returns true or None
         * if no element is found that satisfies the predicate
         */
        public final static Option find(final Func f, final Iterable input)
        {
            if (f == null) throw new IllegalArgumentException("f");
            if (input == null) throw new IllegalArgumentException("input");

            for(final A a : input)
                if(f.apply((a)))
                    return Option.toOption(a);
            return Option.None();
        }

        /**
         * As find except that here we return the zero-based position in the input sequence of the found element
         * findIndex: (A -> bool) -> A list -> int option
         * @param f predicate
         * @param input sequence
         * @param  the type of the element in the input sequence
         * @throws java.lang.IllegalArgumentException if f or input are null
         * @return the position in the input sequence of the first element from the input sequence for which the supplied predicate
         * returns true or None if no element is found that satisfies the predicate
         */
        public static Option findIndex(final Func f, final Iterable input)
        {
            if (f == null) throw new IllegalArgumentException("f");
            if (input == null) throw new IllegalArgumentException("input");

            int pos = 0;
            for (final A a : input)
                if (f.apply(a))
                    return Option.toOption(pos);
                else pos++;
            return Option.None();
        }

        /**
         * As find except that here we return the last element in the input sequence that satisfies the predicate 'f'
         * findLast: (A -> bool) -> A seq -> A option
         * @param f predicate
         * @param input sequence
         * @param  the type of the element in the input sequence
         * @throws java.lang.IllegalArgumentException if f or input are null
         * @return the last element in the input sequence for which the supplied predicate returns true or None
         * if no element is found that satisfies the predicate
         */
        public final static Option findLast(final Func f, final Iterable input)
        {
            if (f == null) throw new IllegalArgumentException("f");
            if (input == null) throw new IllegalArgumentException("input");

            final Pair,Iterable> p = takeNAndYield(input,1);
            final Pair seed = Pair.with(p.getValue0().get(0),f.apply(p.getValue0().get(0)));
            final Pair result = fold(new Func2,A,Pair>(){
                @Override public Pair apply(final Pair state, final A item){return f.apply(item)?Pair.with(item,true):state;}
            },seed,p.getValue1());

            if(result.getValue1()) return Option.toOption(result.getValue0());
            return Option.None();
        }

        /**
         * As find except that here we return the last element in the input sequence that satisfies the predicate 'f'
         * findLast: (A -> bool) -> A list -> A option
         * @param f predicate
         * @param input sequence
         * @param  the type of the element in the input sequence
         * @throws java.lang.IllegalArgumentException if f or input are null
         * @return the last element in the input sequence for which the supplied predicate returns true or None
         * if no element is found that satisfies the predicate
         */
        public final static Option findLast(final Func f, final List input)
        {
            if (f == null) throw new IllegalArgumentException("f");
            if (input == null) throw new IllegalArgumentException("input");

            for (final A a : Iterators.reverse(input))
                if (f.apply(a))
                    return Option.toOption(a);
            return Option.None();
        }

        /**
         * 'pick' is an analogue of find. Instead of a predicate, 'pick' is passed a map function which returns an Option.
         * Each element of the input sequence is supplied in turn to the map function 'f' and the first non-None Option to be returned from
         * the map function is returned by 'pick' to the calling code.
         * pick: (A -> B option) -> A seq -> B option
         *
         * @param f the map function.
         * @param input the input sequence
         * @param  the type of the element in the input sequence
         * @param  the type of the output element
         * @return the first non-None transformed element of the input sequence or None if no such element exists
         */
        public static Option pick(final Func> f, final Iterable input)
        {
            if (f == null) throw new IllegalArgumentException("f");
            if (input == null) throw new IllegalArgumentException("input");

            for(final A a : input)
            {
                final Option intermediate = f.apply(a); // which is, effectively, if(f(a)) return f(a), but without evaluating f twice
                if (!intermediate.isNone())
                    return intermediate;
            }
            return Option.None();
        }

        /**
         * forAll2: the predicate 'f' is applied to all elements in the input sequences input1 and input2 as pairs. If the predicate returns
         * true for all pairs and there is the same number of elements in both input sequences then forAll2 returns true. If the predicate
         * returns false at any point then the traversal of the input sequences halts and forAll2 returns false.
         * forAll2: (A -> B -> bool) -> A list -> B list -> bool option
         * @param f predicate to which each successive pair (input1_i, input2_i) is applied
         * @param input1 input sequence
         * @param input2 input sequence
         * @param  the base type of the element in the first input sequence
         * @param  the base type of the element in the second input sequence
         * @param  the type of the element in the first input sequence
         * @param  the type of the element in the second input sequence
         * @return true if the predicate 'f' evaluates true for all pairs, false otherwise or None
         * if the predicate returns true for all pairs and the sequences contain differing numbers
         * of elements
         */
        public final static Option forAll2(final Func2 f, final Iterable input1, final Iterable input2)
        {
            final Iterator enum1 = input1.iterator();
            final Iterator enum2 = input2.iterator();
            boolean enum1Moved = false, enum2Moved = false;
            do
            {
                enum1Moved = enum1.hasNext();
                enum2Moved = enum2.hasNext();
                if (enum1Moved && enum2Moved && !f.apply(enum1.next(), enum2.next()))
                    return Option.toOption(false);
            } while (enum1Moved && enum2Moved);
            if( enum1Moved != enum2Moved)
                return Option.None();
            return Option.toOption(true);
        }

        /**
         * take: given a list return another list containing the first 'howMany' elements or fewer if there are not enough elements
         * in the input sequence
         * @param howMany a positive upper bound for the number of elements to be returned from the input sequence
         * @param list the input sequence
         * @param  the type of the element in the input sequence
         * @return a list containing the first 'howMany' elements of 'list'
         */
        public static finalList take(final int howMany, final Iterable list)
        {
            if(howMany<0) throw new IllegalArgumentException("Functional.take(int,Iterable): howMany is negative");
            if(list==null) throw new IllegalArgumentException("Functional.take(int,Iterable): list is null");

            if(howMany==0) return new ArrayList(0);

            final List output = new ArrayList(howMany);
            final Iterator iterator = list.iterator();
            for(int i=0;iZip
         * @param l1 input sequence
         * @param l2 input sequence
         * @param  the type of the element in the first input sequence
         * @param  the type of the element in the second input sequence
         * @throws java.lang.IllegalArgumentException
         * @return list of pairs; the first element from each of the two input sequences is the first pair in the output sequence and so on,
         *          in order. If the sequences do not have the same number of elements the results thus far are returned
         */
        public static final List> zip(final Iterable l1, final Iterable l2)
        {
            if(l1==null) throw new IllegalArgumentException("Functional.zip(Iterable,Iterable): l1 is null");
            if(l2==null) throw new IllegalArgumentException("Functional.zip(Iterable,Iterable): l2 is null");

            final List> output = (l1 instanceof Collection && l2 instanceof Collection)
                    ? new ArrayList>(((Collection) l1).size())
                    : new ArrayList>();
            final Iterator l1_it = l1.iterator();
            final Iterator l2_it = l2.iterator();

            while(l1_it.hasNext() && l2_it.hasNext()) output.add(new Pair(l1_it.next(),l2_it.next()));

            return Collections.unmodifiableList(output);
        }

        /**
         * The Convolution operator
         * See Zip
         * @param l1 input sequence
         * @param l2 input sequence
         * @param l3 input sequence
         * @param  the type of the element in the first input sequence
         * @param  the type of the element in the second input sequence
         * @param  the type of the element in the third input sequence
         * @throws java.lang.IllegalArgumentException if any input sequence is null or if the sequences have differing lengths.
         * @return list of triplets; the first element from each of the input sequences is the first triplet in the output sequence and so on,
         *          in order. If the sequences do not have the same number of elements then the results thus far are returned
         */
        public static final List> zip3(final Iterable l1, final Iterable l2, final Iterable l3)
        {
            if(l1==null) throw new IllegalArgumentException("Functional.zip3(Iterable,Iterable,Iterable): l1 is null");
            if(l2==null) throw new IllegalArgumentException("Functional.zip3(Iterable,Iterable,Iterable): l2 is null");
            if(l3==null) throw new IllegalArgumentException("Functional.zip3(Iterable,Iterable,Iterable): l3 is null");

            final List> output = (l1 instanceof Collection && l2 instanceof Collection && l3 instanceof Collection)
                    ? new ArrayList>(((Collection) l1).size())
                    : new ArrayList>();
            final Iterator l1_it = l1.iterator();
            final Iterator l2_it = l2.iterator();
            final Iterator l3_it = l3.iterator();

            while(l1_it.hasNext() && l2_it.hasNext() && l3_it.hasNext()) output.add(new Triplet(l1_it.next(),l2_it.next(),l3_it.next()));

            return Collections.unmodifiableList(output);
        }
    }

    /*
    // Following are control structures, eg if, switch
     */

    /**
     * A functional 'if' statement. Given 'a', evaluate the predicate. If the predicate evaluates to true then return the value of
     * evaluating the 'thenClause' with 'a' otherwise return the value of evaluating the 'elseClause' with 'a'
     * @param a value to be tested with the predicate and thence passed to one of the 'thenClause' and 'elseClause'
     * @param predicate function
     * @param thenClause function
     * @param elseClause function
     * @param  the type of the element which we are passing to the predicate
     * @param  the type of the result of the thenClause and elseClause
     * @return the results of evaluating the 'thenClause' or the 'elseClause', depending on whether the 'predicate' evaluates to true
     * or false respectively
     */
    public static final B If(final A a, final Func predicate, final Func thenClause, final Func elseClause)
    {
        if (a == null) throw new IllegalArgumentException("a");
        if (predicate == null) throw new IllegalArgumentException("predicate");
        if (thenClause == null) throw new IllegalArgumentException("thenClause");
        if (elseClause == null) throw new IllegalArgumentException("elseClause");

        return predicate.apply(a) ? thenClause.apply(a) : elseClause.apply(a);
    }

    /**
     * toCase: a Case builder function.
     * @param pred predicate
     * @param result the result function to be applied if the predicate evaluates to true
     * @param  the type of the element being passed to the predicate
     * @param  the type of the result of the transformation function
     * @return a new Case object
     */
    public static final Case toCase(final Func pred, final Func result)
    {
        if (pred == null) throw new IllegalArgumentException("pred");
        if (result == null) throw new IllegalArgumentException("res");

        return new Case ( pred, result );
    }

    /**
     * Functional switch statement. Provide a sequence of Cases and a function which will be evaluated if none of the Cases are true.
     * @param input the value to be tested
     * @param cases sequence of Case objects
     * @param defaultCase function to be evaluated if none of the Cases are true
     * @param  the type of the element being passed to the predicates in the {@link me.shaftesbury.utils.functional.Case}
     * @param  the type of the result
     * @return the result of the appropriate Case or the result of the 'defaultCase' function
     */
    public static B Switch(final A input, final Iterable> cases, final Func defaultCase)
    {
        return Switch(input,IterableHelper.create(cases),defaultCase);
    }

    /**
     * Functional switch statement. Provide a sequence of Cases and a function which will be evaluated if none of the Cases are true.
     * @param input the value to be tested
     * @param cases sequence of Case objects
     * @param defaultCase function to be evaluated if none of the Cases are true
     * @param  the type of the element passed to the predicate in the {@link me.shaftesbury.utils.functional.Case}
     * @param  the type of the result
     * @return the result of the appropriate Case or the result of the 'defaultCase' function
     */
    public static B Switch(final A input, final Iterable2> cases, final Func defaultCase)
    {
        if (input == null) throw new IllegalArgumentException("input");
        if (cases == null) throw new IllegalArgumentException("cases");
        if (defaultCase == null) throw new IllegalArgumentException("defaultCase");

        //return Try.ToTry(input, a => cases.First(chk => chk.check(a)).results(a), defaultCase);
        try {
            return cases.find(new Func, Boolean>() {
                @Override
                public Boolean apply(final Case abCase) {
                    return abCase.predicate(input);
                }
            }).results(input);
        } catch(final NoSuchElementException k) { return defaultCase.apply(input); }
    }

    /**
     * Helper function to return the first element in a Pair
     * @param  the type of the first element in the pair
     * @param  the type of the second element in the pair
     * @return a function that returns the first element in a Pair
     */
    public static final Func,A> first()
    {
        return new Func, A>() {
            @Override
            public A apply(Pair pair) {
                return pair.getValue0();
            }
        };
    }

    /**
     *  Helper function to return the second element in a Pair
     * @param  the type of the first element in the pair
     * @param  the type of the second element in the pair
     * @return a function that returns the second element in a Pair
     */
    public static final Func,B> second()
    {
        return new Func, B>() {
            @Override
            public B apply(Pair pair) {
                return pair.getValue1();
            }
        };
    }
}