public class RecursiveList<E> implements List<E> {
    private ListNode<E> head = null;
    public E get(int i) {
        if (i < 0) {  throw new IllegalArgumentException();  }
        return get(this.head, i);
    }
    public static <E> E get(ListNode<E> node, int i) {
        if (node == null) {  throw new IndexOutOfBoundsException();  }
        if (i == 0) {
            return node.getElem();
        }
        return get(node.getNext(), i-1);
    }
    public void set(int i, E elem) {
        if (i < 0) {  throw new IllegalArgumentException();  }
        set(this.head, i, elem);
    }
    public static <E> void set(ListNode<E> node, int i, E elem) {
        if (node == null) {  throw new IndexOutOfBoundsException();  }
        if (i == 0) {
            node.setElem(elem);
        } else {
            set(node.getNext(), i-1, elem);
        }
    }
    public int size() {
        return size(this.head);
    }
    public static <E> int size(ListNode<E> node) {
        if (node == null) {  return 0;  }
        return 1+size(node.getNext());
    }
    public void add(E elem) {
        this.head = add(this.head, elem);
    }
    public static <E> ListNode<E> add(ListNode<E> node, E elem) {
        if (node == null) {
            return new ListNode<E>(elem, null);
        }
        node.setNext(add(node.getNext(), elem));
        return node;
    }
    public void add(int i, E elem) {
        if (i < 0) {  throw new IllegalArgumentException();  }
        this.head = add(this.head, i, elem);
    }
    public static <E> ListNode<E> add(ListNode<E> node, int i, E elem) {
        if (i == 0) {
            return new ListNode<E>(elem, node);
        }
        if (node == null) {  throw new IndexOutOfBoundsException();  }
        node.setNext(add(node.getNext(), i-1, elem));
        return node;
    }
    public void remove(int i) {
       if (i < 0) {  throw new IllegalArgumentException();  }
       this.head = remove(this.head, i);
    }
    public static <E> ListNode<E> remove(ListNode<E> node, int i) {
        if (node == null) {  throw new IndexOutOfBoundsException();  }
        if (i == 0) {
            return node.getNext();
        }
        node.setNext(remove(node.getNext(), i-1));
        return node;
    }
    public String toString() {
        return "["+(this.head != null ? this.head.toString() : "") + "]";
    }
}
