package mb.nabl2.util.collections;

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

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) {
        return init.append(this);
    }

    public ConsList<E> append(ConsList<E> tail) {
        ConsList<E> list = tail;
        for(E head : this) {
            list = tail.prepend(head);
        }
        return list;
    }

    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(", ", "[", "]"));
    }

}
@@ -83,16 +83,40 @@
 Arrays;%0A
+import java.util.Deque;%0A
 import j
@@ -208,24 +208,64 @@
 ollectors;%0A%0A
+import com.google.common.collect.Lists;%0A
 import com.g
@@ -1281,90 +1281,88 @@
     
-return init.append(this
+final Deque%3CE%3E elems = Lists.newLinkedList(
 );%0A    
-%7D%0A%0A
     
-public ConsList%3CE%3E append(ConsList%3CE%3E tail) %7B
+init.forEach(elems::push);
 %0A   
@@ -1382,27 +1382,27 @@
 %3CE%3E list = t
-ail
+his
 ;%0A        fo
@@ -1403,25 +1403,30 @@
     
-for(E head : this
+while(!elems.isEmpty()
 ) %7B%0A
@@ -1444,20 +1444,20 @@
  list = 
-tail
+list
 .prepend
@@ -1457,20 +1457,27 @@
 prepend(
-head
+elems.pop()
 );%0A     
@@ -1501,32 +1501,124 @@
 rn list;%0A    %7D%0A%0A
+    public ConsList%3CE%3E append(ConsList%3CE%3E tail) %7B%0A        return tail.prepend(this);%0A    %7D%0A%0A
     public ConsL

Fix append/prepend to not throw away existing tail.