public class MyArrayList<E>
{
    // package access for testing
    Object[] a;
    int size;

    public MyArrayList()
    {
        this(10);
    }

    public MyArrayList(int initialCapacity)
    {
        if (initialCapacity < 0)
            throw new IllegalArgumentException();

        a = new Object[initialCapacity];
        size = 0;
    }

    public int size()
    {
        return size;
    }

    @SuppressWarnings("unchecked")
    public E get(int index)
    {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException();

        return (E) a[index];
    }

    @SuppressWarnings("unchecked")
    public E set(int index, E element)
    {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException();

        Object old = a[index];
        a[index] = element;
        return (E) old;
    }

    public boolean contains(Object elem)
    {
        for (int i = 0; i < size; i++)
        {
            if (elem == null ? a[i] == null : elem.equals(a[i]))
                return true;
        }

        return false;
    }

    public void trimToSize()
    {
        if (size < a.length)
        {
            Object[] b = new Object[size];

            for(int i = 0; i < size; i++)
                b[i] = a[i];

            a = b;
        }
    }

    public void add(int index, E element)
    {
        if (index < 0 || index > size)
            throw new IndexOutOfBoundsException();

        if (size < a.length)
        {
            for(int i = size - 1; i >= index; i--)
                a[i + 1] = a[i];
        }
        else
        {
            Object[] b = new Object[a.length * 2 + 1]; // +1 in case a.length == 0

            for(int i = 0; i < index; i++)
                b[i] = a[i];

            for(int i = index; i < size; i++)
                b[i + 1] = a[i];

            a = b;
        }

        a[index] = element;
        size++;
    }

    public boolean add(E elem)
    {
        add(size, elem);
        return true;
    }

    @SuppressWarnings("unchecked")
    public E remove(int index)
    {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException();

        Object old = a[index];

        for(int i = index + 1; i < size; i++)
            a[i - 1] = a[i];

        size--;
        a[size] = null;

        return (E) old;
    }

    public boolean remove(Object elem)
    {
        for (int i = 0; i < size; i++)
        {
            if((elem == null && a[i] == null) ||
                    (elem != null && elem.equals(a[i]))) 
            {
                remove(i);
                return true;
            }
        }

        return false;
    }

    // start package access methods to facilitate automated testing
    // do not modify these methods

    Object[] getA()
    {
        return a;
    }

    void setA(Object[] newA)
    {
        a = newA;
    }

    int getSize()
    {
        return size;
    }

    void setSize(int newSize)
    {
        size = newSize;
    }

    // end package access methods to facilitate automated testing
}
