public class LinkedList<T> {
    private ListNode<T> headNode;
    private ListNode<T> tailNode;
    private int size;

    public LinkedList() {
        headNode = null;
        tailNode = null;
        size = 0;
    }

    public void add(T value) {
        if (headNode == null) {
            headNode = new ListNode<>(value, null, null);
            tailNode = headNode;
        }
        else {
            ListNode<T> newNode = new ListNode<>(value, null, null);
            newNode.prevNode = tailNode;
            tailNode.nextNode = newNode;
            tailNode = newNode;
        }
        size++;
    }

    public int getSize() {
        return size;
    }

    public T get(int index) {
        if (index < 0 || index > (size - 1)) {
            throw new IllegalArgumentException("Index Out of Bounds");
        }
        if (index < size / 2) {
            int pos = 0;
            ListNode<T> curNode = headNode;
            while (pos < index) {
                curNode = curNode.nextNode;
                pos++;
            }
            return curNode.value;
        }
        else {
            int pos = size - 1;
            ListNode<T> curNode = tailNode;
            while (pos > index) {
                curNode = curNode.prevNode;
                pos--;
            }
            return curNode.value;
        }
    }

    public void remove(int index) {
        if (index < 0 || index > (size - 1)) {
            throw new IllegalArgumentException("Index Out of Bounds");
        }
        if (index == 0) {
            if (size == 1) {
                headNode = null;
                tailNode = null;
            }
            else {
                headNode = headNode.nextNode;
                headNode.prevNode = null;
            }
            size--;
            return;
        }
        if (index == (size - 1)) {
            tailNode = tailNode.prevNode;
            tailNode.nextNode = null;
            size--;
            return;
        }

        if (index < size / 2) {
            int pos = 0;
            ListNode<T> curNode = headNode;
            while (pos < index) {
                curNode = curNode.nextNode;
                pos++;
            }
            curNode.nextNode.prevNode = curNode.prevNode;
            curNode.prevNode.nextNode = curNode.nextNode;
            size--;
        }
        else {
            int pos = size - 1;
            ListNode<T> curNode = tailNode;
            while (pos > index) {
                curNode = curNode.prevNode;
                pos--;
            }
            curNode.nextNode.prevNode = curNode.prevNode;
            curNode.prevNode.nextNode = curNode.nextNode;
            size--;
        }
    }
}
