package org.metaborg.util.collection;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Deque;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;

import com.google.common.collect.Lists;
import com.google.common.collect.Streams;

public class ConsList<E> implements Iterable<E>, Serializable {
    private static final long serialVersionUID = 1L;

    private final E head;
    private final ConsList<E> tail;

    private ConsList(E head, ConsList<E> tail) {
        this.head = head;
        this.tail = tail;
    }

    @Override public Iterator<E> iterator() {
        return new Iterator<E>() {

            private ConsList<E> current = ConsList.this;

            @Override public boolean hasNext() {
                return !current.isNil();
            }

            @Override public E next() {
                if(current.isNil()) {
                    throw new NoSuchElementException();
                }
                final E next = current.head;
                current = current.tail;
                return next;
            }
        };
    }

    public ConsList<E> prepend(E head) {
        return new ConsList<>(head, this);
    }

    public ConsList<E> prepend(ConsList<E> init) {
        final Deque<E> elems = Lists.newLinkedList();
        for(E e : init) {
            elems.push(e);
        }
        ConsList<E> list = this;
        while(!elems.isEmpty()) {
            list = list.prepend(elems.pop());
        }
        return list;
    }

    public ConsList<E> append(ConsList<E> tail) {
        return tail.prepend(this);
    }

    public ConsList<E> tail() {
        return isNil() ? this : tail;
    }

    private boolean isNil() {
        return head == null;
    }

    public static <E> ConsList<E> nil() {
        return new ConsList<>(null, null);
    }

    public static <E> ConsList<E> of(E e) {
        return new ConsList<>(e, nil());
    }

    @SafeVarargs public static <E> ConsList<E> of(E... es) {
        return of(Arrays.asList(es));
    }

    public static <E> ConsList<E> of(Iterable<E> es) {
        if(es instanceof ConsList) {
            return (ConsList<E>) es;
        }
        ConsList<E> list = nil();
        for(E e : es) {
            list = list.prepend(e);
        }
        return list;
    }

    @Override public String toString() {
        return Streams.stream(this).map(Object::toString).collect(Collectors.joining(", ", "[", "]"));
    }

}
@@ -137,16 +137,39 @@
 erator;%0A
+import java.util.List;%0A
 import j
@@ -1236,32 +1236,248 @@
 , this);%0A    %7D%0A%0A
+    public ConsList%3CE%3E prepend(List%3CE%3E init) %7B%0A        ConsList%3CE%3E list = this;%0A        for(int i = init.size() - 1; i %3C= 0; i--) %7B%0A            list = list.prepend(init.get(i));%0A        %7D%0A        return list;%0A    %7D%0A%0A
     public ConsL

Add optimized version for prepending lists.