diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..c0071b2 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..712ab9d --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..9dc782b --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/java/fundamentals/ResizingArrayQueue.java b/src/main/java/fundamentals/ResizingArrayQueue.java new file mode 100644 index 0000000..5120099 --- /dev/null +++ b/src/main/java/fundamentals/ResizingArrayQueue.java @@ -0,0 +1,153 @@ +package fundamentals; + +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * We are interested in the implementation of a Queue using an array. + * The array size needs to be updated when it is full or close to empty + * i.e. when the array is full, the size of the array should double + * and when the array is less than 1/4 full, the size of the array should halve. + * + * You are asked to implement this Queue by completing the class see (TODO's) + * Most important methods are: + * - the enqueue method to add an element + * - the remove method [The NoSuchElementException is thrown when the queue is empty] + * - the iterator used to browse the queue in FIFO + * + * Hint : when the head of the stack reaches the end of the array, go back at its beginning. + * + * @param + */ +public class ResizingArrayQueue implements Iterable { + + public Item[] q; + private long nOp = 0; + // BEGIN STRIP + private int head; + private int tail; + private int size; + // END STRIP + + @SuppressWarnings("unchecked") + public ResizingArrayQueue() { + q = (Item[]) new Object[2]; + nOp = 0; + // BEGIN STRIP + head = 0; + tail = 0; + size = 0; + // END STRIP + } + + public boolean isEmpty() { + // TODO + // Student return false; + // BEGIN STRIP + return size == 0; + // END STRIP + } + + public int size() { + // TODO + // Student return 0; + // BEGIN STRIP + return size; + // END STRIP + } + + private long getnOp() { + return nOp; + } + // BEGIN STRIP + @SuppressWarnings("unchecked") + private void resize(int capacity) { + nOp++; + Item[] temp = (Item[]) new Object[capacity]; + for (int i = 0; i < size; i++) { + temp[i] = q[(head + i) % q.length]; + } + q = temp; + head = 0; + tail = size; + } + // END STRIP + + /** + * Add an item at the tail of the queue. + * Resize the array if needed. + * @param item the item to add. + */ + public void enqueue(Item item) { + // TODO + // BEGIN STRIP + nOp++; + if (size == q.length) { + resize(2 * q.length); + } + q[tail] = item; + tail = (tail + 1) % q.length; + size++; + // END STRIP + } + + /** + * Removes and return the element at the head of the queue. + * Resize the array if needed. + * @return The item freshly removed. + * @throws NoSuchElementException when the queue is empty. + */ + public Item dequeue() throws NoSuchElementException{ + // TODO + // STUDENT return null; + // BEGIN STRIP + nOp++; + if (isEmpty()) throw new NoSuchElementException(); + + Item val = q[head]; + q[head] = null; + head = (head + 1) % q.length; + size--; + + if (size > 0 && size == q.length / 4) { + resize(q.length / 2); + } + + return val; + // END STRIP + } + + /** + * Returns an iterator that iterates through the items in FIFO order. + * @return an iterator that iterates through the items in FIFO order. + */ + @Override + public Iterator iterator() { + // TODO + // STUDENT return null; + // BEGIN STRIP + return new QueueIterator(); + // END STRIP + } + + // BEGIN STRIP + private class QueueIterator implements Iterator { + private final long nOp = getnOp(); + private int currentHead = head; + + @Override + public boolean hasNext() { + return currentHead != tail; + } + + @Override + public Item next() { + if (nOp != getnOp()) throw new ConcurrentModificationException(); + Item current = q[currentHead]; + currentHead = (currentHead + 1) % q.length; + return current; + } + } + // END STRIP +} diff --git a/src/test/java/fundamentals/ResizingArrayQueueTest.java b/src/test/java/fundamentals/ResizingArrayQueueTest.java new file mode 100644 index 0000000..d7c3469 --- /dev/null +++ b/src/test/java/fundamentals/ResizingArrayQueueTest.java @@ -0,0 +1,122 @@ +package fundamentals; + +import org.javagrader.Grade; +import org.junit.jupiter.api.Test; + +import java.util.ConcurrentModificationException; +import java.util.Iterator; + +import static org.junit.jupiter.api.Assertions.*; + +@Grade +public class ResizingArrayQueueTest { + + @Test + @Grade(value = 1, cpuTimeout = 1) + public void testBasicEnqueueDequeue() { + ResizingArrayQueue q = new ResizingArrayQueue<>(); + + assertTrue(q.isEmpty()); + q.enqueue(1); + q.enqueue(2); + q.enqueue(3); + + assertEquals(3, q.size()); + assertEquals(Integer.valueOf(1), q.dequeue()); + assertEquals(Integer.valueOf(2), q.dequeue()); + assertEquals(Integer.valueOf(3), q.dequeue()); + assertTrue(q.isEmpty()); + } + + @Test + @Grade(value = 1, cpuTimeout = 1) + public void testWrapAround() { + ResizingArrayQueue q = new ResizingArrayQueue<>(); + + for (int i = 0; i < 10; i++) q.enqueue(i); + for (int i = 0; i < 5; i++) assertEquals(Integer.valueOf(i), q.dequeue()); + for (int i = 10; i < 15; i++) q.enqueue(i); + + for (int i = 5; i < 15; i++) assertEquals(Integer.valueOf(i), q.dequeue()); + assertTrue(q.isEmpty()); + } + + @Test + @Grade(value = 1, cpuTimeout = 1) + public void testResizeDown() { + ResizingArrayQueue q = new ResizingArrayQueue<>(); + + for (int i = 0; i < 32; i++) q.enqueue(i); + Object[] arr = q.q; + int oldCap = arr.length; + assertTrue(oldCap >= 32); + + while (q.size() > oldCap / 4 + 1) q.dequeue(); + q.dequeue(); // should shrink here + + arr = q.q; + int len = arr.length; + assertEquals(oldCap / 2, len); + } + + @Test + @Grade(value = 1, cpuTimeout = 1) + public void testResizeUp() { + ResizingArrayQueue q = new ResizingArrayQueue<>(); + + q.enqueue(1); + q.enqueue(2); + Object[] arr = q.q; + int len = arr.length; + assertEquals(2, len); + + q.enqueue(3); // should trigger resize + arr = q.q; + len = arr.length; + assertEquals(4, len); + + q.enqueue(4); + q.enqueue(5); // trigger resize again + arr = q.q; + len = arr.length; + assertEquals(8, len); + } + + @Test + @Grade(value = 1, cpuTimeout = 1) + public void testIteratorOrderAndWrap() { + ResizingArrayQueue q = new ResizingArrayQueue<>(); + + q.enqueue(1); + q.enqueue(2); + q.enqueue(3); + q.enqueue(4); + q.dequeue(); // head moves + q.dequeue(); // head moves + q.enqueue(5); + q.enqueue(6); + + int[] expected = {3, 4, 5, 6}; + int idx = 0; + + for (int x : q) { + assertEquals(expected[idx++], x); + } + } + + @Test + @Grade(value = 1, cpuTimeout = 1) + public void testIteratorFailFast() { + ResizingArrayQueue q = new ResizingArrayQueue<>(); + q.enqueue(1); + q.enqueue(2); + q.enqueue(3); + + Iterator it = q.iterator(); + assertTrue(it.hasNext()); + assertEquals(1, it.next()); + + q.enqueue(4); // structural modification => should break iterator + assertThrows(ConcurrentModificationException.class, it::next); + } +}