diff --git a/src/datastructures/worklists/MinFourHeap.java b/src/datastructures/worklists/MinFourHeap.java new file mode 100644 index 0000000000000000000000000000000000000000..bb02bd635d57d268aa12da0d8dcaf8c3fc5254b6 --- /dev/null +++ b/src/datastructures/worklists/MinFourHeap.java @@ -0,0 +1,47 @@ +package datastructures.worklists; + +import cse332.exceptions.NotYetImplementedException; +import cse332.interfaces.worklists.PriorityWorkList; + +/** + * See cse332/interfaces/worklists/PriorityWorkList.java + * for method specifications. + */ +public class MinFourHeap<E extends Comparable<E>> extends PriorityWorkList<E> { + /* Do not change the name of this field; the tests rely on it to work correctly. */ + private E[] data; + + public MinFourHeap() { + throw new NotYetImplementedException(); + } + + @Override + public boolean hasWork() { + throw new NotYetImplementedException(); + } + + @Override + public void add(E work) { + throw new NotYetImplementedException(); + } + + @Override + public E peek() { + throw new NotYetImplementedException(); + } + + @Override + public E next() { + throw new NotYetImplementedException(); + } + + @Override + public int size() { + throw new NotYetImplementedException(); + } + + @Override + public void clear() { + throw new NotYetImplementedException(); + } +} diff --git a/src/p2/wordsuggestor/NGramToNextChoicesMap.java b/src/p2/wordsuggestor/NGramToNextChoicesMap.java index 2f72f87fac756bc29ed0f01a367c647752bd10c6..74201965d0730b24468808c957aaa3904755e1eb 100644 --- a/src/p2/wordsuggestor/NGramToNextChoicesMap.java +++ b/src/p2/wordsuggestor/NGramToNextChoicesMap.java @@ -1,6 +1,7 @@ package p2.wordsuggestor; import java.util.Comparator; +import java.util.Iterator; import java.util.function.Supplier; import cse332.datastructures.containers.Item; @@ -26,7 +27,18 @@ public class NGramToNextChoicesMap { * Increments the count of word after the particular NGram ngram. */ public void seenWordAfterNGram(NGram ngram, String word) { - throw new NotYetImplementedException(); + Dictionary<AlphabeticString, Integer> counter = map.find((NGram) ngram); + if (counter == null) { + counter = newInner.get(); + map.insert((NGram) ngram, counter); + } + + Integer prev = counter.find(new AlphabeticString(word)); + if (prev == null) { + prev = 0; + } + counter.insert(new AlphabeticString(word), prev + 1); + } /** @@ -39,7 +51,23 @@ public class NGramToNextChoicesMap { * @return An array of all the Items for the requested ngram. */ public Item<String, Integer>[] getCountsAfter(NGram ngram) { - throw new NotYetImplementedException(); + if (ngram == null) { + return (Item<String, Integer>[]) new Item[0]; + } + Dictionary<AlphabeticString, Integer> counter = map.find((NGram) ngram); + Item<String, Integer>[] result = (Item<String, Integer>[]) new Item[counter != null + ? counter.size() : 0]; + if (counter != null) { + Iterator<Item<AlphabeticString, Integer>> it = counter.iterator(); + + for (int i = 0; i < result.length; i++) { + Item<AlphabeticString, Integer> item = it.next(); + result[i] = new Item<String, Integer>(item.key.toString(), + item.value); + } + } + return result; + } public String[] getWordsAfter(NGram ngram, int k) { diff --git a/src/tests/gitlab/ckpt1/Ckpt1Tests.java b/src/tests/gitlab/ckpt1/Ckpt1Tests.java index 68c6d3185c569256827e63824beffa03d10f8103..0bc8b47cdc1b5af9b1960bcbe1da5225f64b87a8 100644 --- a/src/tests/gitlab/ckpt1/Ckpt1Tests.java +++ b/src/tests/gitlab/ckpt1/Ckpt1Tests.java @@ -1,12 +1,13 @@ package tests.gitlab.ckpt1; +import datastructures.worklists.MinFourHeap; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ - NGramToNextChoicesMapTests.class, + MinFourHeap.class, MoveToFrontListTests.class, CircularArrayComparatorTests.class }) diff --git a/src/tests/gitlab/ckpt1/MinFourHeapTests.java b/src/tests/gitlab/ckpt1/MinFourHeapTests.java new file mode 100644 index 0000000000000000000000000000000000000000..76d93630798d508b5541cb0b5d1b9087f7c88828 --- /dev/null +++ b/src/tests/gitlab/ckpt1/MinFourHeapTests.java @@ -0,0 +1,171 @@ +package tests.gitlab.ckpt1; + +import cse332.interfaces.worklists.PriorityWorkList; +import datastructures.worklists.MinFourHeap; +import org.junit.Before; +import org.junit.Test; +import tests.gitlab.ckpt1.WorklistGradingTests; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.Random; + +import static org.junit.Assert.*; + +public class MinFourHeapTests extends WorklistGradingTests { + private static Random RAND; + + @Before + public void init() { + STUDENT_STR = new MinFourHeap<>(); + STUDENT_DOUBLE = new MinFourHeap<>(); + STUDENT_INT = new MinFourHeap<>(); + RAND = new Random(42); + } + + @Test(timeout = 3000) + public void testHeapWith5Items() { + PriorityWorkList<String> heap = new MinFourHeap<>(); + String[] tests = { "a", "b", "c", "d", "e" }; + for (int i = 0; i < 5; i++) { + String str = tests[i] + "a"; + heap.add(str); + } + + for (int i = 0; i < 5; i++) { + String str_heap = heap.next(); + String str = (char) ('a' + i) + "a"; + assertEquals(str, str_heap); + } + } + + @Test(timeout = 3000) + public void testOrderingDoesNotMatter() { + PriorityWorkList<String> ordered = new MinFourHeap<>(); + PriorityWorkList<String> reversed = new MinFourHeap<>(); + PriorityWorkList<String> random = new MinFourHeap<>(); + + addAll(ordered, new String[]{"a", "b", "c", "d", "e"}); + addAll(reversed, new String[]{"e", "d", "c", "b", "a"}); + addAll(random, new String[]{"d", "b", "c", "e", "a"}); + + assertTrue(isSame("a", ordered.peek(), reversed.peek(), random.peek())); + assertTrue(isSame("a", ordered.next(), reversed.next(), random.next())); + assertTrue(isSame("b", ordered.next(), reversed.next(), random.next())); + + addAll(ordered, new String[] {"a", "a", "b", "c", "z"}); + addAll(reversed, new String[] {"z", "c", "b", "a", "a"}); + addAll(random, new String[] {"c", "z", "a", "b", "a"}); + + String[] expected = new String[] {"a", "a", "b", "c", "c", "d", "e", "z"}; + for (String e : expected) { + assertTrue(isSame(e, ordered.peek(), reversed.peek(), random.peek())); + assertTrue(isSame(e, ordered.next(), reversed.next(), random.next())); + } + } + + private boolean isSame(String... args) { + String first = args[0]; + for (String arg : args) { + if (!first.equals(arg)) { + return false; + } + } + return true; + } + + @Test(timeout = 3000) + public void testHugeHeap() { + PriorityWorkList<String> heap = new MinFourHeap<>(); + int n = 10000; + + // Add them + for (int i = 0; i < n; i++) { + String str = String.format("%05d", i * 37 % n); + heap.add(str); + } + // Delete them all + for (int i = 0; i < n; i++) { + String s = heap.next(); + assertEquals(i , Integer.parseInt(s)); + } + } + + @Test(timeout = 3000) + public void testWithCustomComparable() { + PriorityWorkList<Coordinate> student = new MinFourHeap<>(); + Queue<Coordinate> reference = new PriorityQueue<>(); + + for (int i = 0; i < 10000; i++) { + Coordinate coord = new Coordinate(RAND.nextInt(10000) - 5000, RAND.nextInt(10000) - 5000); + student.add(coord); + reference.add(coord); + } + assertEquals(reference.size(), student.size()); + + while (!reference.isEmpty()) { + assertEquals(reference.peek() , student.peek()); + assertEquals(reference.remove() , student.next()); + } + } + + public static class Coordinate implements Comparable<Coordinate> { + private int x; + private int y; + + public Coordinate(int x, int y) { + this.x = x; + this.y = y; + } + + // What exactly this comparable method is doing is somewhat arbitrary. + public int compareTo(Coordinate other) { + if (this.x != other.x) { + return this.x - other.x; + } else { + return this.y - other.y; + } + } + } + + @Test(timeout = 3000) + public void checkStructure() { + PriorityWorkList<Integer> heap = new MinFourHeap<>(); + addAll(heap, new Integer[] {10, 10, 15, 1, 17, 16, 100, 101, 102, 103, 105, 106, 107, 108}); + + Object[] heapData = getField(heap, "data"); + String heapStr = Arrays.toString(heapData); + String heapExp = "[1, 10, 15, 10, 17, 16, 100, 101, 102, 103, 105, 106, 107, 108"; + + heap.next(); + heap.next(); + heap.next(); + + Object[] heapData2 = getField(heap, "data"); + String heapStr2 = Arrays.toString(heapData2); + String heapExp2 = "[15, 16, 103, 107, 17, 108, 100, 101, 102, 106, 105,"; + + assertTrue(heapStr.contains(heapExp)); + assertTrue(heapStr2.contains(heapExp2)); + } + + protected <T> T getField(Object o, String fieldName) { + try { + Field field = o.getClass().getSuperclass().getDeclaredField(fieldName); + field.setAccessible(true); + Object f = field.get(o); + return (T) f; + } catch (Exception var6) { + try { + Field field = o.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + Object f = field.get(o); + return (T) f; + } catch (Exception var5) { + return null; + } + } + } +} diff --git a/src/tests/gitlab/ckpt1/NGramToNextChoicesMapTests.java b/src/tests/gitlab/ckpt1/NGramToNextChoicesMapTests.java deleted file mode 100644 index 85d65dc7e1e1e41267cfe08ebf716c6fe814827e..0000000000000000000000000000000000000000 --- a/src/tests/gitlab/ckpt1/NGramToNextChoicesMapTests.java +++ /dev/null @@ -1,172 +0,0 @@ -package tests.gitlab.ckpt1; - -import cse332.datastructures.containers.Item; -import cse332.datastructures.trees.BinarySearchTree; -import cse332.types.NGram; -import org.junit.Test; -import p2.wordsuggestor.NGramToNextChoicesMap; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.Map; -import java.util.TreeMap; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -public class NGramToNextChoicesMapTests { - private NGramToNextChoicesMap init() { - return new NGramToNextChoicesMap(BinarySearchTree::new, BinarySearchTree::new); - } - - @Test(timeout = 3000) - public void testOneWordPerNGram() { - NGramToNextChoicesMap map = init(); - NGram[] ngrams = new NGram[]{ - new NGram(new String[]{"foo", "bar", "baz"}), - new NGram(new String[]{"fee", "fi", "fo"}), - new NGram(new String[]{"a", "s", "d"}) - }; - - String[] words = new String[]{"bop", "fum", "f"}; - for (int i = 0; i < ngrams.length; i++) { - map.seenWordAfterNGram(ngrams[i], words[i]); - } - for (int i = 0; i < ngrams.length; i++) { - Item<String, Integer>[] items = map.getCountsAfter(ngrams[i]); - assertEquals(1, items.length); - Item<String, Integer> item = items[0]; - assertEquals(words[i], item.key); - assertEquals(1, item.value.intValue()); - } - } - - @Test(timeout = 3000) - public void testMultipleWordsPerNGram() { - NGramToNextChoicesMap map = init(); - NGram[] ngrams = new NGram[]{ - new NGram(new String[]{"foo", "bar", "baz"}), - new NGram(new String[]{"fee", "fi", "fo"}), - new NGram(new String[]{"four", "score", "and"}), - new NGram(new String[]{"3", "2", "2"}), - new NGram(new String[]{"a", "s", "d"}) - }; - - String[][] words = new String[][] { - new String[]{"bip", "bop", "bzp"}, - new String[]{"fum", "giants"}, - new String[]{"ago", "seven", "years"}, - new String[]{"new", "thrown", "uuu", "zzz"}, - new String[]{"do", "for", "while"} - }; - - for (int i = 0; i < ngrams.length; i++) { - for (int j = 0; j < words[i].length; j++) { - map.seenWordAfterNGram(ngrams[i], words[i][j]); - } - - } - for (int i = 0; i < ngrams.length; i++) { - Item<String, Integer>[] items = map.getCountsAfter(ngrams[i]); - String[] answer = words[i]; - assertEquals(answer.length, items.length); - String[] itemsWithoutCounts = new String[items.length]; - for (int j = 0; j < answer.length; j++) { - assertEquals(1, items[j].value.intValue()); - itemsWithoutCounts[j] = items[j].key; - } - Arrays.sort(itemsWithoutCounts); - assertArrayEquals(answer, itemsWithoutCounts); - } - } - - @Test(timeout = 3000) - public void testGetNonexistentNGram() { - NGramToNextChoicesMap map = init(); - NGram[] ngrams = new NGram[]{ - new NGram(new String[]{"foo", "bar", "baz"}), - new NGram(new String[]{"fee", "fi", "fo"}), - new NGram(new String[]{"a", "s", "d"}) - }; - - String[] words = new String[]{"bop", "fum", "f"}; - for (int i = 0; i < ngrams.length; i++) { - map.seenWordAfterNGram(ngrams[i], words[i]); - } - Item<String, Integer>[] items = map.getCountsAfter(new NGram(new String[] { "yo" })); - assertNotNull(items); - assertEquals(0, items.length); - } - - @SuppressWarnings("unchecked") - @Test(timeout = 3000) - public void testRepeatedWordsPerNGram() { - NGramToNextChoicesMap map = init(); - // Creates Ngrams to test for with N = 3 - NGram[] ngrams = new NGram[]{ - new NGram(new String[]{"foo", "bar", "baz"}), - new NGram(new String[]{"fee", "fi", "fo"}), - new NGram(new String[]{"four", "score", "and"}), - new NGram(new String[]{"3", "2", "2"}), - new NGram(new String[]{"a", "s", "d"}) - }; - // Array of words seen after each Ngram with correlating index from above - String[][] words = new String[][] { - new String[]{"bop", "bip", "boop", "bop", "bop"}, - new String[]{"fum", "giants", "giants"}, - new String[]{"seven", "years", "years", "ago", "ago"}, - new String[]{"throw", "throw", "throw", "throw", "throw"}, - new String[]{"for", "while", "do", "do", "while", "for"} - }; - - // yes this is awful, but i can't think of a better way to do it atm - // Creates answers for getCountsAfter - Word seen after and count - // corrlates with words and ngrams above - // Note that words after are in sorted order, not in order of array in words - Map<NGram, Item<String, Integer>[]> answers = new TreeMap<>(); - answers.put(ngrams[0], (Item<String, Integer>[]) new Item[]{ - new Item<>("bip", 1), - new Item<>("boop", 1), - new Item<>("bop", 3) - }); - - answers.put(ngrams[1], (Item<String, Integer>[]) new Item[]{ - new Item<>("fum", 1), - new Item<>("giants", 2) - }); - - answers.put(ngrams[2], (Item<String, Integer>[]) new Item[]{ - new Item<>("ago", 2), - new Item<>("seven", 1), - new Item<>("years", 2) - }); - - answers.put(ngrams[3], (Item<String, Integer>[]) new Item[]{ - new Item<>("throw", 5) - }); - - answers.put(ngrams[4], (Item<String, Integer>[]) new Item[]{ - new Item<>("do", 2), - new Item<>("for", 2), - new Item<>("while", 2) - }); - - // Adds nGrams and words after to student's NGramToNextChoicesMap - for (int i = 0; i < ngrams.length; i++) { - for (int j = 0; j < words[i].length; j++) { - map.seenWordAfterNGram(ngrams[i], words[i][j]); - } - } - - // checks to see if getCountsAfter returns correctly - for (int i = 0; i < ngrams.length; i++) { - NGram ngram = ngrams[i]; - Item<String, Integer>[] results = map.getCountsAfter(ngram); - Arrays.sort(results, Comparator.comparing(r -> r.key)); - Item<String, Integer>[] expected = answers.get(ngram); - // checks for correct number of unique words after - assertArrayEquals(expected, results); - } - } -} diff --git a/src/tests/gitlab/ckpt1/WorklistGradingTests.java b/src/tests/gitlab/ckpt1/WorklistGradingTests.java new file mode 100644 index 0000000000000000000000000000000000000000..c1bb91933d6d0991daef31a4c4da54f92aa868d6 --- /dev/null +++ b/src/tests/gitlab/ckpt1/WorklistGradingTests.java @@ -0,0 +1,99 @@ +package tests.gitlab.ckpt1; + +import cse332.interfaces.worklists.WorkList; +import org.junit.Test; + +import java.util.NoSuchElementException; + +import static org.junit.Assert.*; + +public abstract class WorklistGradingTests { + protected static WorkList<String> STUDENT_STR; + protected static WorkList<Double> STUDENT_DOUBLE; + protected static WorkList<Integer> STUDENT_INT; + + @Test(timeout = 3000) + public void testHasWork() { + assertFalse(STUDENT_INT.hasWork()); + } + + @Test(timeout = 3000) + public void testHasWorkAfterAdd() { + STUDENT_INT.add(1); + assertTrue(STUDENT_INT.hasWork()); + } + + @Test(timeout = 3000) + public void testHasWorkAfterAddRemove() { + for (int i = 0; i < 1000; i++) { + STUDENT_DOUBLE.add(Math.random()); + } + for (int i = 0; i < 1000; i++) { + STUDENT_DOUBLE.next(); + } + assertFalse(STUDENT_DOUBLE.hasWork()); + } + @Test(timeout = 3000) + public void testPeekHasException() { + assertTrue(doesPeekThrowException(STUDENT_INT)); + + addAndRemove(STUDENT_INT, 42, 10); + assertTrue(doesPeekThrowException(STUDENT_INT)); + } + + @Test(timeout = 3000) + public void testNextHasException() { + assertTrue(doesNextThrowException(STUDENT_INT)); + + addAndRemove(STUDENT_INT, 42, 10); + assertTrue(doesNextThrowException(STUDENT_INT)); + } + @Test(timeout = 3000) + public void testClear() { + addAll(STUDENT_STR, new String[]{"Beware", "the", "Jabberwock", "my", "son!"}); + + assertTrue(STUDENT_STR.hasWork()); + assertEquals(5, STUDENT_STR.size()); + + STUDENT_STR.clear(); + assertFalse(STUDENT_STR.hasWork()); + assertEquals(0, STUDENT_STR.size()); + assertTrue(doesPeekThrowException(STUDENT_STR)); + assertTrue(doesNextThrowException(STUDENT_STR)); + } + + // UTILITY METHODS + + protected static <E> void addAll(WorkList<E> worklist, E[] values) { + for (E value : values) { + worklist.add(value); + } + } + + protected static <E> void addAndRemove(WorkList<E> worklist, E value, int amount) { + for (int i = 0; i < amount; i++) { + worklist.add(value); + } + for (int i = 0; i < amount; i++) { + worklist.next(); + } + } + + protected static <E> boolean doesPeekThrowException(WorkList<E> worklist) { + try { + worklist.peek(); + } catch (NoSuchElementException e) { + return true; + } + return false; + } + + protected static <E> boolean doesNextThrowException(WorkList<E> worklist) { + try { + worklist.next(); + } catch (NoSuchElementException e) { + return true; + } + return false; + } +}