import java.util.Random;

public abstract class CollectionTimer {
    public static final int[] DEFAULT_MUTATIONS = {10000, -10000};
    private Random elemGen = new Random();

    public CollectionTimer() {
        elemGen.setSeed(0);
    }

    public CollectionTimer(long elemGenSeed) {
        elemGen.setSeed(elemGenSeed);
    }

    public abstract void addElement(Integer elem);
    public abstract void removeElement();
    public abstract int getSize();
    public abstract boolean isEmpty();

    /* Insert 'amount' number of random elements in datastructure.  Note that
     * the random elements are being generated inside the inner loop, so
     * that aspect is also being measured for performance.  However, in an
     * experiment where only the random numbers were being generated, without
     * invoking the data structure, it shows that the complexity of generating
     * so called random numbers is linear, O(n):

    andreas@soh:~/ds11/% java -Xint Assignment1 100000
    ArrayList:    61
    [...]
    andreas@soh:~/ds11/% java -Xint Assignment1 200000
    ArrayList:    116

    * Enabling the data structure yields these timings:

    andreas@soh:~/ds11/% java -Xint Assignment1 100000
    ArrayList:    218
    [...]
    andreas@soh:~/ds11/% java -Xint Assignment1 200000
    ArrayList:    492
    [...]

     * By rounding liberally, the effect can be summed up as follows:
     * Complexity of adding 100000 elements: 220 - 60 = 180
     * Complexity of adding 200000 elements: 480 - 120 = 360
     *
     * And 360 - 180 > 120 - 60.
     *
     * So the complexity of the data structure is clearly greater than that of
     * the random number generator (on this computer).
     */
    public void insert(int amount) {
        for (int a = 0; a <  amount; a++) {
            addElement(elemGen.nextInt());
        }
    }

    public boolean extract(int amount) {
        for (int a = 0; a <  amount; a++) {
            removeElement();
        }

        return true;
    }

    public long time() {
        long begin = System.currentTimeMillis();
        insert(DEFAULT_MUTATIONS[0]);
        extract(-DEFAULT_MUTATIONS[1]);
        return System.currentTimeMillis() - begin;
    }

    public long time(int[] mutations) {
        long begin = System.currentTimeMillis();

        for (int a : mutations) {
            if (a < 0) {
		try {
                    extract(-a);
                } catch(RuntimeException E) {
                    throw new RuntimeException(
                        "Extraction failed.\n" + E.toString());
                }
            } else {
		try {
                    insert(a);
                } catch(RuntimeException E) {
                    throw new RuntimeException(
                        "Insertion failed.\n" + E.toString());
                }
            }
        }

        return System.currentTimeMillis() - begin;
    }
}
