- {
*
* @return this Bag as an array of Objects.
*/
- Item[] asArray();
+ Object[] asArray();
+
+ /**
+ * Method to get this Bag as an array of the same component type as the one
+ * supplied.
+ *
+ * The array carries the type information that {@link #asArray()} cannot: Item
+ * is erased, so there is nothing at runtime to say what kind of array to
+ * allocate, and the caller supplying one settles it. This is exactly
+ * {@link java.util.Collection#toArray(Object[])}, and for exactly the same
+ * reason — call it with {@code new Item[0]} to get a genuinely typed array
+ * back.
+ *
+ * @param a an array of the desired component type. It is filled and returned
+ * if it is big enough; otherwise a new one of the same type is
+ * allocated.
+ * @param the component type.
+ * @return this Bag as an array of T.
+ */
+ T[] asArray(T[] a);
/**
* Empty out this Bag
*/
void clear();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Bag_Array.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Bag_Array.java
index 81b10f66..c780e5fd 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Bag_Array.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Bag_Array.java
@@ -4,6 +4,7 @@
package com.phasmidsoftware.dsaipg.adt.bqs;
+import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Random;
@@ -25,8 +26,7 @@ public class Bag_Array- implements Bag
- {
* @param random a random source.
*/
public Bag_Array(Random random) {
- //noinspection unchecked
- grow((Item[]) new Object[0], 32);
+ allocate(INITIAL_CAPACITY);
this.random = random;
}
@@ -39,6 +39,32 @@ public Bag_Array() {
this(new Random());
}
+ /**
+ * Construct a Bag_Array containing the given items.
+ *
+ * NOTE this deliberately does not call {@link #growFrom}: it allocates storage
+ * large enough at the outset. That matters because growFrom is an exercise. A
+ * bag which could not even be constructed until the exercise was written made
+ * every test that merely uses a bag depend on it -- of the tests blocked that
+ * way, two thirds were testing graphs and classification sorts, not bags.
+ * Now only genuine growth, past the initial capacity, needs the exercise.
+ *
+ * The new bag has room to spare, so adding one more item does not immediately
+ * force a growth.
+ *
+ * @param items the items to put in the bag.
+ * @param the item type.
+ * @return a Bag_Array containing exactly those items.
+ */
+ @SafeVarargs
+ public static Bag_Array of(T... items) {
+ Bag_Array result = new Bag_Array<>();
+ if (items.length * 2 > INITIAL_CAPACITY) result.allocate(items.length * 2);
+ System.arraycopy(items, 0, result.items, 0, items.length);
+ result.count = items.length;
+ return result;
+ }
+
/**
* Adds the specified item to the bag.
* If the internal storage is full, it will be expanded to accommodate the new item.
@@ -85,8 +111,14 @@ public void clear() {
* @return true if the item is found in the bag, otherwise false
*/
public boolean contains(Item item) {
- for (Item i : items) {
- if (i != null && i.equals(item))
+ // NOTE the first count entries only. Scanning the whole backing array
+ // meant that after clear(), which resets count and nothing else, this
+ // still answered true for items the bag no longer held -- while
+ // multiplicity, which happens to start with an isEmpty() guard, answered
+ // zero. Two methods disagreeing about the same question.
+ for (int i = 0; i < count; i++) {
+ Item x = items[i];
+ if (x != null && x.equals(item))
return true;
}
return false;
@@ -100,9 +132,9 @@ public boolean contains(Item item) {
*/
public int multiplicity(Item item) {
int result = 0;
- if (isEmpty()) return 0;
- for (Item i : items) {
- if (i != null && i.equals(item))
+ for (int i = 0; i < count; i++) {
+ Item x = items[i];
+ if (x != null && x.equals(item))
result++;
}
return result;
@@ -115,22 +147,62 @@ public int multiplicity(Item item) {
*/
public Iterator- iterator() {
assert items != null; // Should be not-null any time after construction.
- return new UnorderedIterator<>(asArray(), random);
+ // NOTE the cast is safe HERE and nowhere else. Inside a generic class Item
+ // erases to Object, so this compiles to nothing at all; it is only at a
+ // call site which knows what Item is that the compiler emits a checkcast
+ // and the Object[] is caught out. That asymmetry is what the old asArray
+ // javadoc was reaching for.
+ @SuppressWarnings("unchecked") Item[] typed = (Item[]) asArray();
+ return new UnorderedIterator<>(typed, random);
}
/**
* Method to get this Bag as an array.
*
- * Internally, Object[] can be cast as an Item[] but it is not valid externally.
- * Hence, the arraycopy.
- * This is a quirk of Java Generics.
+ * NOTE Object[], not Item[], and deliberately. Item is erased, so there is
+ * nothing at runtime to say what kind of array to allocate; the array really
+ * is an Object[] however it is declared. Declaring it Item[] promised
+ * something unobtainable — every use of the result at that type threw
+ * ClassCastException, including {@code asArray().length} and a for-each with
+ * the element type, because the compiler inserts a checkcast wherever it knows
+ * what Item is. Only widening to Object[] worked, which is why every caller
+ * already does. The old javadoc credited the arraycopy with fixing this; it
+ * did not, since it copied into another Object[].
+ *
+ * {@link java.util.Collection#toArray()} returns Object[] for the same reason,
+ * and offers {@link java.util.Collection#toArray(Object[])} for a caller who
+ * has somewhere typed to put the result. Add the equivalent here if one is
+ * ever wanted.
*
* @return this Bag as an array.
*/
- public Item[] asArray() {
- @SuppressWarnings("unchecked") Item[] items = (Item[]) new Object[count];
- System.arraycopy(this.items, 0, items, 0, count);
- return items;
+ public Object[] asArray() {
+ Object[] result = new Object[count];
+ System.arraycopy(items, 0, result, 0, count);
+ return result;
+ }
+
+ /**
+ * Method to get this Bag as an array of the supplied array's component type.
+ *
+ * Follows {@link java.util.Collection#toArray(Object[])}: the supplied array is
+ * filled and returned if it is big enough, and the element just past the
+ * contents is set to null so a caller can find the end; otherwise a new array
+ * of the same component type is allocated. Passing {@code new Item[0]} is the
+ * usual idiom and always allocates.
+ *
+ * @param a an array of the desired component type.
+ * @param the component type.
+ * @return this Bag as an array of T.
+ */
+ @SuppressWarnings("unchecked")
+ public T[] asArray(T[] a) {
+ T[] result = a.length >= count
+ ? a
+ : (T[]) Array.newInstance(a.getClass().getComponentType(), count);
+ System.arraycopy(items, 0, result, 0, count);
+ if (result.length > count) result[count] = null;
+ return result;
}
@Override
@@ -152,14 +224,30 @@ private void grow(Item[] source, int size) {
items = growFrom(source, size);
}
+ /**
+ * Allocate fresh, empty storage of the given size, discarding whatever was
+ * there. Used at construction, where there is nothing to copy.
+ *
+ * @param size the size of the new array.
+ */
+ private void allocate(int size) {
+ //noinspection unchecked
+ items = (Item[]) new Object[size];
+ }
+
/**
* Retrieves the current capacity of the internal storage for the bag.
* The capacity is defined as the size of the array used to store items.
* It represents the maximum number of items the bag can hold without resizing.
*
+ * NOTE package-private rather than private so that BagTest can assert that
+ * growth DOUBLES the capacity. That property cannot be observed from outside:
+ * an implementation which grew by a constant instead would satisfy every other
+ * test, while making n additions cost O(n^2) rather than amortized O(n).
+ *
* @return the capacity of the internal storage.
*/
- private int capacity() {
+ int capacity() {
assert items != null; // Should be not-null any time after construction.
return items.length;
}
@@ -179,17 +267,29 @@ private boolean full() {
* copies all the elements of from into the start of the resulting array,
* then returns the result.
*
+ * NOTE package-private rather than private so that BagTest can test this
+ * contract directly. Testing it only through {@code add} is not enough: add
+ * always calls {@code grow(items, 2 * capacity())}, where the source is the
+ * whole backing array, so an implementation returning an array of
+ * {@code from.length * 2} is accidentally right at every call site while
+ * ignoring the size it was given.
+ *
* @param from the source array
* @param size the size of the new array
*/
- private static T[] growFrom(T[] from, int size) {
+ static T[] growFrom(T[] from, int size) {
// TO BE IMPLEMENTED grow array and copy
- return null;
- // END SOLUTION
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
+ /**
+ * The capacity a bag starts with. Growth doubles this, and only that doubling
+ * goes through growFrom.
+ */
+ private static final int INITIAL_CAPACITY = 32;
+
private final Random random;
private Item[] items = null;
private int count = 0;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/DList.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/DList.java
index 22031f5e..4b33d2f8 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/DList.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/DList.java
@@ -89,7 +89,7 @@ public void remove(Item item) throws BQSException {
*/
public void addBeforeElement(Item item, D_Element next) {
// TO BE IMPLEMENTED
- throw new RuntimeException("implementation missing");
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -100,7 +100,7 @@ public void addBeforeElement(Item item, D_Element next) {
*/
public void addAfterElement(Item item, D_Element prev) {
// TO BE IMPLEMENTED
- throw new RuntimeException("implementation missing");
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -110,19 +110,17 @@ public void addAfterElement(Item item, D_Element prev) {
*/
public void remove(D_Element element) {
// TO BE IMPLEMENTED
- throw new RuntimeException("implementation missing");
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
public D_Element findFirst(Item item) {
// TO BE IMPLEMENTED
- return null;
- // END SOLUTION
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
public D_Element findLast(Item item) {
// TO BE IMPLEMENTED
- return null;
- // END SOLUTION
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -215,4 +213,4 @@ public Iterator- iterator() {
private D_Element head = null;
private D_Element tail = null;
private int count = 0;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Dictionary.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Dictionary.java
index 80340329..914727b6 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Dictionary.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Dictionary.java
@@ -59,4 +59,4 @@ public interface Dictionary
{
* @return a Set of keys present in the dictionary.
*/
Set keySet();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Dictionary_Hash.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Dictionary_Hash.java
index 44cb7bf3..39b1cee7 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Dictionary_Hash.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Dictionary_Hash.java
@@ -81,4 +81,4 @@ public Set keySet() {
}
private final Map map = new HashMap<>();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Element.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Element.java
index ddd38a71..8bd73e12 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Element.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Element.java
@@ -78,4 +78,4 @@ public String toString() {
* (not just at the head).
*/
Element- next;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/LazyList.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/LazyList.java
index ae71b999..93706cde 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/LazyList.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/LazyList.java
@@ -138,4 +138,4 @@ public static LazyList
from(final int start) {
// return from(start, 1);
// }
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/LinkedList.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/LinkedList.java
index 5bdd44ad..9e878066 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/LinkedList.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/LinkedList.java
@@ -20,4 +20,4 @@ public interface LinkedList- extends Iterable
- , ListLike
- {
*/
Item getHead();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/LinkedList_Elements.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/LinkedList_Elements.java
index 6201e8cc..27b32c9d 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/LinkedList_Elements.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/LinkedList_Elements.java
@@ -126,4 +126,4 @@ private Iterable
- asItemIterable() {
* Initially, it is set to {@code null}, indicating that the list is empty.
*/
private Element
- head = null;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/ListLike.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/ListLike.java
index a19b997f..04cb5e24 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/ListLike.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/ListLike.java
@@ -28,4 +28,4 @@ public interface ListLike
- {
* @return true if this list is empty.
*/
boolean isEmpty();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Queue.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Queue.java
index 19e8d35c..e0f7749f 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Queue.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Queue.java
@@ -33,4 +33,4 @@ public interface Queue
- extends Iterable
- {
* @return true if this stack is empty
*/
boolean isEmpty();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Queue_Array.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Queue_Array.java
index faadaf27..49086f3e 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Queue_Array.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Queue_Array.java
@@ -1,6 +1,7 @@
package com.phasmidsoftware.dsaipg.adt.bqs;
import java.util.Arrays;
+import java.util.List;
import java.util.Iterator;
/**
@@ -51,7 +52,10 @@ public boolean isEmpty() {
public Iterator
- iterator() {
assert items != null; // Should be not-null any time after construction.
// NOTE: there is no Java-defined array iterator.
- return Arrays.asList(asArray()).iterator();
+ // The cast is safe here for the same reason as in Bag_Array.iterator: Item
+ // is erased inside this class, so it compiles to nothing.
+ @SuppressWarnings("unchecked") List
- list = (List
- ) Arrays.asList(asArray());
+ return list.iterator();
}
/**
@@ -68,15 +72,15 @@ public int size() {
/**
* Package-scope method to get the contents of this Queue_Array as an array.
*
- * NOTE: Internally, Object[] can be cast as an Item[] but it is not valid externally.
- * Hence, the arraycopy.
- * This is a quirk of Java Generics.
+ * NOTE: Object[], not Item[]. See Bag_Array.asArray for why: Item is erased,
+ * so the array really is an Object[] however it is declared, and declaring it
+ * Item[] promises a type no caller can actually use.
*
* @return this Queue as an array.
*/
- Item[] asArray() {
+ Object[] asArray() {
int count = size();
- @SuppressWarnings("unchecked") Item[] items = (Item[]) new Object[count];
+ Object[] items = new Object[count];
if (straddle() && !isEmpty()) {
int l = n - i;
System.arraycopy(this.items, i, items, 0, l);
@@ -206,4 +210,4 @@ private boolean straddle() {
* to manage the circular indexing of the queue and to determine its current size.
*/
private int n;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Queue_Elements.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Queue_Elements.java
index a2bf20df..4c075e28 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Queue_Elements.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Queue_Elements.java
@@ -29,8 +29,7 @@ public class Queue_Elements- implements SizedIterable
- , Queue
- {
*/
public void offer(Item item) {
// TO BE IMPLEMENTED
-
- // END SOLUTION
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -44,8 +43,7 @@ public Item poll() {
if (isEmpty()) return null;
else {
// TO BE IMPLEMENTED
- return null;
- // END SOLUTION
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
}
@@ -149,4 +147,4 @@ public Item next() {
* FIFO (First-In-First-Out) operations on the queue.
*/
private Element
- newest;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/ShuntingYard.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/ShuntingYard.java
index ecc03dd8..57fd0cff 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/ShuntingYard.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/ShuntingYard.java
@@ -190,4 +190,4 @@ private void operate() throws BQSException {
* allowing dynamic resizing and efficient operations without a fixed storage limit.
*/
private final Stack
valStack = new Stack_LinkedList<>();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Stack.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Stack.java
index 70e8d6be..6cb91861 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Stack.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/Stack.java
@@ -39,4 +39,4 @@ public interface Stack- extends Iterable
- {
* @return true if this stack is empty
*/
boolean isEmpty();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/UnorderedIterator.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/UnorderedIterator.java
index f6c49517..d0712daa 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/UnorderedIterator.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/bqs/UnorderedIterator.java
@@ -148,4 +148,4 @@ private static
List copyCollection(Collection collection) {
* is required based on a Random source.
*/
private final Random random;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/pq/PQException.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/pq/PQException.java
index 1d7f7cf8..38758cf3 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/pq/PQException.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/pq/PQException.java
@@ -18,4 +18,4 @@ public class PQException extends Exception {
public PQException(String msg) {
super(msg);
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/pq/PriorityQueue.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/pq/PriorityQueue.java
index c7c13b9a..fdff8707 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/pq/PriorityQueue.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/pq/PriorityQueue.java
@@ -65,4 +65,4 @@ public interface PriorityQueue {
*/
@SuppressWarnings("unused")
boolean getMax();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/pq/PriorityQueue_BinaryHeap.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/pq/PriorityQueue_BinaryHeap.java
index 3ebbec2f..6b11021b 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/pq/PriorityQueue_BinaryHeap.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/pq/PriorityQueue_BinaryHeap.java
@@ -371,4 +371,4 @@ public boolean getMax() {
*/
private final boolean floyd;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/BaseImmutableSymbolTable.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/BaseImmutableSymbolTable.java
index e19f68fd..df41b8a5 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/BaseImmutableSymbolTable.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/BaseImmutableSymbolTable.java
@@ -74,4 +74,4 @@ public BaseImmutableSymbolTable(ST map, Supplier defaultSuppl
* the key being queried is absent.
*/
private final Supplier defaultSupplier;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/ImmutableSymbolTable.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/ImmutableSymbolTable.java
index 68daba99..23dff80b 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/ImmutableSymbolTable.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/ImmutableSymbolTable.java
@@ -78,4 +78,4 @@ default Value getOrDefault(Key key, Supplier defaultValueFunction) {
* @return the current size.
*/
int size();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/FrequencyCounter.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/FrequencyCounter.java
index 250cc4c6..19d9c3a1 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/FrequencyCounter.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/FrequencyCounter.java
@@ -48,7 +48,7 @@ public double relativeFrequencyAsPercentage(Key key) {
public void increment(Key s) {
validateKey(s);
// TO BE IMPLEMENTED
- throw new RuntimeException("implementation missing");
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -83,4 +83,4 @@ public String toString() {
@SuppressWarnings("CanBeFinal")
private long total = 0L;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/HashTable.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/HashTable.java
index a44022de..ad9c7381 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/HashTable.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/HashTable.java
@@ -152,4 +152,4 @@ interface HasNext {
abstract Element next();
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/HashTable_LP.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/HashTable_LP.java
index 595e9312..cc68de64 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/HashTable_LP.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/HashTable_LP.java
@@ -9,6 +9,13 @@
/**
* Class which implements ST (symbol table) by using Linear Probing (Open Addressing).
+ *
+ * NOTE given capacity for a million keys up front, this is as quick as {@code java.util.HashMap}
+ * -- 0.056s against 0.059s for a million puts and gets. Written out in Python the same algorithm
+ * takes 1.73s, while Python's own {@code dict}, whose inner loop is in C, takes 0.29s. So the cost
+ * of writing a hash table rather than calling one is about 6x in Python and nothing at all in Java.
+ * That is the reason idiomatic Python leans so hard on its libraries. See
+ * {@code docs/Java vs Python.md}.
*
* @param the key type.
* @param the value type.
@@ -219,4 +226,4 @@ public HashTableException(String s) {
super(s);
}
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/HashTable_SC.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/HashTable_SC.java
index 0bfac555..83c06c35 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/HashTable_SC.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/HashTable_SC.java
@@ -215,4 +215,4 @@ public Node next() {
return next;
}
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/STMap.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/STMap.java
index 57bb4ac7..3b7a362c 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/STMap.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/hashtable/STMap.java
@@ -93,4 +93,4 @@ public String toString() {
}
private final Map map;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BST.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BST.java
index bf3662be..998b95ca 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BST.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BST.java
@@ -38,4 +38,4 @@ default Set keys() {
* @return the original value
*/
Value delete(Key key);
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BSTBenchmark.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BSTBenchmark.java
index d8193012..18976637 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BSTBenchmark.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BSTBenchmark.java
@@ -225,4 +225,4 @@ void setMeanDepth(final int nodes, final double meanDepth) {
final static LazyLogger logger = new LazyLogger(BSTBenchmark.class);
private final int nRuns;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BSTOptimisedDeletion.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BSTOptimisedDeletion.java
index 71ce24d8..1c9161bf 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BSTOptimisedDeletion.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BSTOptimisedDeletion.java
@@ -340,8 +340,7 @@ private Node updateCount(Node other) {
private Node delete(Key k) {
// CONSIDER using navigate
// TO BE IMPLEMENTED
- return null;
- // END SOLUTION
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -660,3 +659,4 @@ public DepthException() {
}
}
+
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BSTSimple.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BSTSimple.java
index d416cc0f..732d710b 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BSTSimple.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BSTSimple.java
@@ -261,8 +261,7 @@ private void evaluateCount(Node x) {
@NotNull
private Node hibbardDeletion(Node x) {
// TO BE IMPLEMENTED
- return x;
- // END SOLUTION
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -520,4 +519,4 @@ private static class DepthException extends Exception {
public DepthException() {
}
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BstDetail.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BstDetail.java
index 7ce9d1df..dd526504 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BstDetail.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/symbolTable/tree/BstDetail.java
@@ -49,4 +49,4 @@ public interface BstDetail, Value> extends BST intsSupplier(int safetyFactor) {
private final int n;
private final int m;
private final Random random;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSum.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSum.java
index 19e9aca6..9978f210 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSum.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSum.java
@@ -7,6 +7,13 @@
* Requirements:
* - The input array should be sorted and contain distinct elements for optimal performance.
* - If the implementation can handle unsorted or non-distinct arrays, this will depend on the specific implementation details.
+ *
+ * NOTE these three implementations are also the clearest place in the repository to see
+ * that a growth rate belongs to the algorithm and not to the language. Timing each of them
+ * at a series of sizes recovers the exponents 3, 2 and 2 in Java and in Python alike, even
+ * though Python takes some forty times longer to get there. Measured numbers, and the point
+ * at which Python's quadratic overtakes Java's cubic — around n = 500 — are in
+ * {@code docs/Java vs Python.md}.
*/
public interface ThreeSum {
/**
@@ -18,4 +25,4 @@ public interface ThreeSum {
* @return an ordered, distinct, array of Triple.
*/
Triple[] getTriples();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumBenchmark.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumBenchmark.java
index 8e3e6d84..d0d5a205 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumBenchmark.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumBenchmark.java
@@ -103,7 +103,7 @@ public static void main(String[] args) {
private void benchmarkThreeSum(final String description, final Consumer function, int n, final TimeLogger[] timeLoggers) {
if (description.equals("ThreeSumCubic") && n > 4000) return;
// TO BE IMPLEMENTED
- throw new RuntimeException("implementation missing");
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -150,4 +150,4 @@ private void benchmarkThreeSum(final String description, final Consumer f
private final Supplier supplier;
private final int n;
private final Config config;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumCubic.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumCubic.java
index 27766d28..865e3292 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumCubic.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumCubic.java
@@ -43,4 +43,4 @@ public Triple[] getTriples() {
private final int[] a;
private final int length;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumQuadratic.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumQuadratic.java
index a49d12b5..d169b49b 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumQuadratic.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumQuadratic.java
@@ -46,9 +46,9 @@ public Triple[] getTriples() {
List getTriples(int j) {
List triples = new ArrayList<>();
// TO BE IMPLEMENTED : for each candidate, test if a[i] + a[j] + a[k] = 0.
- throw new RuntimeException("implementation missing");
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
private final int[] a;
private final int length;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumQuadrithmic.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumQuadrithmic.java
index ca5dafe9..159e0e6a 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumQuadrithmic.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/ThreeSumQuadrithmic.java
@@ -58,10 +58,9 @@ public Triple[] getTriples() {
*/
Triple getTriple(int i, int j) {
// TO BE IMPLEMENTED : use binary search to find the third element
- // END SOLUTION
- return null;
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
private final int[] a;
private final int length;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/Triple.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/Triple.java
index 7929e7a4..79d04392 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/Triple.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/Triple.java
@@ -90,4 +90,4 @@ public Triple(int x, int y, int z) {
final int x;
final int y;
final int z;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSum.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSum.java
index 7ddd1f69..efc5787e 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSum.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSum.java
@@ -16,4 +16,4 @@ public interface TwoSum {
* @return an ordered, distinct, array of Pair.
*/
Pair[] getPairs();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSumBenchmark.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSumBenchmark.java
index c565a41b..ad9f001e 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSumBenchmark.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSumBenchmark.java
@@ -95,7 +95,7 @@ public static void main(String[] args) {
private void benchmarkTwoSum(final String description, final Consumer function, int n, final TimeLogger[] timeLoggers) {
if (n > 8000) return;
// TO BE IMPLEMENTED
- throw new RuntimeException("implementation missing");
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -115,4 +115,4 @@ private void benchmarkTwoSum(final String description, final Consumer fun
private final Supplier supplier;
private final int n;
private final Config config;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSumQuadratic.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSumQuadratic.java
index 6f7abd2d..96845987 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSumQuadratic.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSumQuadratic.java
@@ -45,4 +45,4 @@ public Pair[] getPairs() {
private final int[] a;
private final int length;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSumWithCalipers.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSumWithCalipers.java
index f557e642..f260adc9 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSumWithCalipers.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/threesum/TwoSumWithCalipers.java
@@ -54,8 +54,8 @@ public Pair[] getPairs() {
public static List calipers(int[] a, Function function) {
List pairs = new ArrayList<>();
// TO BE IMPLEMENTED : implement getPairs
- throw new RuntimeException("implementation missing");
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
private final int[] xs;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/trie/Trie.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/trie/Trie.java
index f9f58093..60344e2a 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/trie/Trie.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/trie/Trie.java
@@ -140,3 +140,4 @@ private boolean delete(TrieNode current, String word, int index) {
}
}
+
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/trie/TrieBenchmark.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/trie/TrieBenchmark.java
index 1995baa1..b5657308 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/trie/TrieBenchmark.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/trie/TrieBenchmark.java
@@ -63,3 +63,4 @@ public static void main(String[] args) throws IOException {
System.out.printf("Trie delete time: %d ns%n", trieDeleteTime);
}
}
+
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/trie/TrieNode.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/trie/TrieNode.java
index 7ee66f36..5d55ae7e 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/trie/TrieNode.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/adt/trie/TrieNode.java
@@ -46,3 +46,4 @@ public TrieNode() {
*/
boolean isEndOfWord;
}
+
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/HuffmanCoding.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/HuffmanCoding.java
index 76c08b7d..8a323997 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/HuffmanCoding.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/HuffmanCoding.java
@@ -94,9 +94,12 @@ public Code encode(Code code) {
long val = code.value;
int len = code.length;
if (available < len) {
- int shiftVal = 64 - len + available;
- result = new Code(val << shiftVal >> shiftVal, len - available);
- val = val >> (len - available);
+ int carried = len - available;
+ // NOTE a Code value is unsigned, so mask the carried-over bits out rather
+ // than shifting them left and back: >> would sign-extend a remainder whose
+ // top bit is set, and set every bit above it in the next Value.
+ result = new Code(val & ((1L << carried) - 1), carried);
+ val = val >> carried;
len = available;
}
encode(val, len);
@@ -169,8 +172,7 @@ public void close() {
static class Code {
public Code add(int x) {
// TO BE IMPLEMENTED
- return null;
- // END SOLUTION
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
@Override
@@ -219,9 +221,11 @@ public Long[] encode(String[] symbols) {
throw new RuntimeException("unknown symbol: " + symbol);
Code result = current.encode(code);
if (result != null) {
+ // NOTE the new Value receives the remainder, which is the part of code
+ // that did not fit. The bits that did fit are already in the closed Value.
values.add(current);
current = new Value(0L);
- current.encode(code);
+ current.encode(result);
}
}
current.close();
@@ -235,7 +239,13 @@ public Long[] encode(String[] symbols) {
/**
* Retrieves the Huffman-encoded {@code Code} associated with the given key.
* If no code is found for the provided key, the method attempts to retrieve
- * the code by appending a zero-width joiner (U+FE0F) character to the key.
+ * the code by appending a variation selector (U+FE0F) to the key, which is
+ * how the four suit symbols are held in the table.
+ *
+ * NOTE this makes encoding many-to-one: "S" and "S️" encode alike. The
+ * decoder emits whichever form the tree holds, so a round trip returns the
+ * suit symbols with their selector even when the input lacked it. Compare
+ * with the selector stripped, as {@code HuffmanCodingTest} does.
*
* @param key the symbol for which to retrieve the corresponding {@code Code}
* @return the {@code Code} associated with the given key, or {@code null}
@@ -279,8 +289,7 @@ public HuffmanEncoder(Node node) {
private Map getEncoder(Node node) {
Map result = new HashMap<>();
// TO BE IMPLEMENTED
- // END SOLUTION
- return result;
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -334,9 +343,8 @@ public String decode(long[] array) {
* or {@code null} if the decoding process encounters an invalid state
*/
private Node decode(StringBuilder stringBuilder, Node state, long x) {
- // TO BE IMPLEMENTED
- // END SOLUTION
- return state;
+ // TO BE IMPLEMENTED : consume all 64 bits of x, one per step down the tree, emitting a symbol at each leaf
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -363,6 +371,13 @@ public HuffmanDecoder(Node node) {
public static class Node implements Comparable {
/**
* Compares this Node with the specified Node for order based on their frequency values.
+ *
+ * NOTE frequency alone, so equal-frequency nodes are ordered arbitrarily by the
+ * PriorityQueue. Every resulting code is optimal and they all have the same expected
+ * length, but which of two equally common symbols gets the shorter code is not
+ * determined. Do not expect a particular codebook, and do not compare an encoding
+ * against a stored one. The Python port breaks the tie by insertion order, so its
+ * tree is reproducible and differs from this one.
*
* @param o the Node to be compared with this Node.
* @return a negative integer, zero, or a positive integer as this Node's frequency
@@ -398,7 +413,7 @@ public boolean isLeaf() {
*/
public void dfs(BiFunction depthFunction, BiConsumer consumer, Object depthIndicator, Integer branch) {
// TO BE IMPLEMENTED
- // END SOLUTION
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -579,4 +594,4 @@ public static void showEncoder(final HuffmanEncoder encoder) {
System.out.println(encoder.toString());
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/codelength/CodeLength.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/codelength/CodeLength.java
index 34f945e5..a9e38942 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/codelength/CodeLength.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/codelength/CodeLength.java
@@ -28,4 +28,4 @@ public static void main(String[] args) {
" is " + s1.work1(n, i));
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/codelength/Solution.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/codelength/Solution.java
index 888dc701..24db20a6 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/codelength/Solution.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/codelength/Solution.java
@@ -45,4 +45,4 @@ public double work1(int n, int length) {
}
private final double[][][] f = new double[2][5100][53];
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/runLengthEncoding/RunLength.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/runLengthEncoding/RunLength.java
index 5789f919..4fa01178 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/runLengthEncoding/RunLength.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/compression/runLengthEncoding/RunLength.java
@@ -69,4 +69,4 @@ public RunLength(double p_pixel_black) {
}
private final double p_pixel_black;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/DAG.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/DAG.java
index cbdef690..121dd68a 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/DAG.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/DAG.java
@@ -50,4 +50,4 @@ public interface DAG {
*/
Iterable sorted();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/DAG_Impl.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/DAG_Impl.java
index fe446abf..7eaa5296 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/DAG_Impl.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/DAG_Impl.java
@@ -5,7 +5,7 @@
import com.phasmidsoftware.dsaipg.util.iteration.SizedIterable;
import java.util.Random;
-import java.util.TreeSet;
+import java.util.HashSet;
import java.util.function.Consumer;
/**
@@ -26,7 +26,7 @@ public class DAG_Impl extends DiGraph implements DAG {
* @param post a consumer function that is executed on each vertex after all its neighbors have been visited.
*/
public void dfs(V vertex, Consumer pre, Consumer post) {
- new DepthFirstSearch(new TreeSet<>(), pre, post).innerDfs(vertex);
+ new DepthFirstSearch(new HashSet<>(), pre, post).innerDfs(vertex);
}
/**
@@ -39,19 +39,6 @@ public Iterable sorted() {
return reversePostOrderDFS();
}
- /**
- * Retrieves all the edges of the directed acyclic graph (DAG).
- * This method iterates through all adjacency lists and collects all edges into a single collection.
- *
- * @return a SizedIterable containing all the edges of the graph.
- */
- public SizedIterable> edges() {
- Bag> result = new Bag_Array<>(random);
- for (Bag> b : adjacentEdges.values())
- for (Edge e : b)
- result.add(e);
- return result;
- }
/**
* Adds a directed edge to the graph by updating the adjacency list for the source vertex
@@ -78,17 +65,6 @@ public void addEdge(V from, V to, E attributes) {
addEdge(new Edge<>(from, to, attributes));
}
- /**
- * Retrieves or initializes the adjacency bag for a given vertex in the graph.
- * An adjacency bag contains all the edges that originate from the specified vertex.
- * If the adjacency bag for the vertex does not exist, it is created and stored.
- *
- * @param vertex the vertex for which the adjacency bag is requested.
- * @return the Bag of edges adjacent to the given vertex.
- */
- protected Bag> getAdjacencyBag(V vertex) {
- return adjacentEdges.computeIfAbsent(vertex, k -> new Bag_Array<>(random));
- }
/**
* Constructor for the DAG_Impl class.
@@ -97,7 +73,7 @@ protected Bag> getAdjacencyBag(V vertex) {
* @param random a Random instance used for probabilistic or randomized operations.
*/
public DAG_Impl(Random random) {
- this.random = random;
+ super(random);
}
/**
@@ -106,14 +82,8 @@ public DAG_Impl(Random random) {
* It serves as a convenience constructor to create a DAG_Impl without providing a specific random number generator.
*/
public DAG_Impl() {
- this(new Random());
+ super();
}
- /**
- * A final instance of {@link Random} used for generating random numbers in the DAG_Impl class.
- * This instance facilitates randomized operations, such as probabilistic graph processing
- * or randomized graph traversal algorithms.
- */
- private final Random random;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/DiGraph.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/DiGraph.java
index 87459538..a00ecd93 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/DiGraph.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/DiGraph.java
@@ -21,13 +21,58 @@
*/
public class DiGraph extends AbstractGraph> {
+ /**
+ * Construct a DiGraph with an explicit source of randomness.
+ *
+ * NOTE the Random matters because the adjacency bags iterate in a deliberately
+ * random order, so a traversal of this graph is only repeatable if the bags
+ * are. Without this constructor a DiGraph's bags each got their own unseeded
+ * Random, which is why DiGraphTest could not be made deterministic and why
+ * three DAGTest methods were commented out with "this fails because bags are
+ * iterated randomly now". DAG_Impl had it and DiGraph did not, for no reason
+ * beyond where it was first needed.
+ *
+ * @param random the source of randomness for the adjacency bags.
+ */
+ public DiGraph(Random random) {
+ this.random = random;
+ }
+
+ /**
+ * Construct a DiGraph with its own unseeded source of randomness.
+ */
+ public DiGraph() {
+ this(new Random());
+ }
+
+ /**
+ * Retrieve or create the adjacency bag for a vertex.
+ *
+ * NOTE overridden so that the bag gets THIS graph's Random, which
+ * AbstractGraph knows nothing about.
+ *
+ * @param vertex the vertex.
+ * @return its adjacency bag, created if absent.
+ */
+ @Override
+ protected Bag> getAdjacencyBag(V vertex) {
+ return adjacentEdges.computeIfAbsent(vertex, k -> new Bag_Array<>(random));
+ }
+
/**
* Reverse the sense of this DAG.
*
* @return a DAG whose edges all point in the opposite direction to those in this DAG.
*/
public DiGraph reverse() {
- DiGraph result = new DiGraph<>();
+ DiGraph result = new DiGraph<>(random);
+ // NOTE the vertices must be carried over first, and separately. Rebuilding
+ // from the edges alone loses any vertex with no edge at either end -- and
+ // since kernelDAG() walks reverse().reversePostOrderDFS(), such a vertex
+ // was then left out of the strongly-connected-component decomposition
+ // altogether, though it is a component of its own. A graph of one lone
+ // vertex produced no kernels at all.
+ for (V v : vertices()) result.getAdjacencyBag(v);
for (Edge e : edges()) result.addEdge(e.reverse());
return result;
}
@@ -51,7 +96,7 @@ public void addEdge(Edge edge) {
* @return a {@code SizedIterable} containing all edges in the graph.
*/
public SizedIterable> edges() {
- Bag> result = new Bag_Array<>();
+ Bag> result = new Bag_Array<>(random);
for (Iterable> b : adjacentEdges.values())
for (Edge e : b)
result.add(e);
@@ -67,7 +112,7 @@ public SizedIterable> edges() {
*/
protected Stack reversePostOrderDFS() {
Stack postOrderStack = new Stack_LinkedList<>();
- new DepthFirstSearch(new TreeSet<>(), null, postOrderStack::push).innerDfs();
+ new DepthFirstSearch(new HashSet<>(), null, postOrderStack::push).innerDfs();
return postOrderStack;
}
@@ -83,7 +128,7 @@ protected Stack reversePostOrderDFS() {
*/
DAG, E> kernelDAG() {
final DAG_Impl, E> result = new DAG_Impl<>(new Random(0L));
- final TreeSet marked = new TreeSet<>();
+ final Set marked = new HashSet<>();
for (V vertex : reverse().reversePostOrderDFS()) {
Kernel kernel = new Kernel<>();
new DepthFirstSearch(marked, kernel::add, null).innerDfs(vertex);
@@ -105,6 +150,11 @@ public String toString() {
return adjacentEdges.toString();
}
+ /**
+ * The source of randomness for the adjacency bags.
+ */
+ protected final Random random;
+
/**
* This class implements Depth First Search (DFS) traversal for a graph.
* It is designed to operate on a directed graph (DiGraph) and allows for both pre-order and post-order processing of vertices during the traversal.
@@ -119,12 +169,12 @@ class DepthFirstSearch {
* The traversal allows optional pre-order and post-order Consumer functions to be
* executed on vertices during traversal. Either one of the Consumers (pre or post) must be non-null.
*
- * @param marked a TreeSet to keep track of visited vertices to prevent cycles and revisits.
+ * @param marked a Set to keep track of visited vertices to prevent cycles and revisits.
* @param pre a Consumer function that is executed on each vertex before visiting its adjacent vertices (pre-order action).
* @param post a Consumer function that is executed on each vertex after all its adjacent vertices have been visited (post-order action).
* @throws RuntimeException if both pre and post Consumers are null.
*/
- public DepthFirstSearch(TreeSet marked, Consumer pre, Consumer post) {
+ public DepthFirstSearch(Set marked, Consumer pre, Consumer post) {
this.pre = pre;
this.post = post;
this.marked = marked;
@@ -169,14 +219,14 @@ void innerDfs(V v) {
if (marked.contains(v)) return;
marked.add(v);
if (pre != null) pre.accept(v);
- for (Edge e : adjacentEdges.get(v)) {
+ for (Edge e : adjacent(v)) {
V v1 = e.getTo();
if (!marked.contains(v1)) innerDfs(v1);
}
if (post != null) post.accept(v);
}
- private final TreeSet marked;
+ private final Set marked;
private final Consumer pre;
private final Consumer post;
}
@@ -230,4 +280,4 @@ public String toString() {
return vertices.toString();
}
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/Edge.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/Edge.java
index 0a99a431..e324a783 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/Edge.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dag/Edge.java
@@ -67,4 +67,4 @@ public Edge(V from, V to, E attributes) {
private final V from;
private final V to;
private final E attributes;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/Dijkstra.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/Dijkstra.java
index 69345604..baae78ee 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/Dijkstra.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/Dijkstra.java
@@ -343,4 +343,4 @@ private void validateVertex(int v) {
* It is a fixed constant set at the time of constructing the Dijkstra object.
*/
private final int n;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/DirectedEdge.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/DirectedEdge.java
index e7dbe57a..d9c9d898 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/DirectedEdge.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/DirectedEdge.java
@@ -88,4 +88,4 @@ public DirectedEdge(int v, int w, double weight) {
* typically used to signify cost, distance, or capacity in an edge-weighted graph.
*/
private final double weight;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/EdgeWeightedDigraph.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/EdgeWeightedDigraph.java
index cf07fe18..4794976f 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/EdgeWeightedDigraph.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/EdgeWeightedDigraph.java
@@ -227,3 +227,4 @@ public EdgeWeightedDigraph(EdgeWeightedDigraph G) {
private final int[] indegree; // indegree[v] = indegree of vertex v
}
+
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/IndexMinPQ.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/IndexMinPQ.java
index c2350012..b58dc8ce 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/IndexMinPQ.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dijkstra/IndexMinPQ.java
@@ -354,4 +354,4 @@ public Integer next() {
private final int[] qp; // inverse of pq - qp[pq[i]] = pq[qp[i]] = i
private final Key[] keys; // keys[i] = priority of i
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/coins/CoinChange.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/coins/CoinChange.java
index 13fdf431..631e0d75 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/coins/CoinChange.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/coins/CoinChange.java
@@ -172,4 +172,4 @@ public Node(int amount) {
}
private final int[] coins;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/houseRobber/houseRobber.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/houseRobber/houseRobber.java
index e4206cc9..98a6a853 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/houseRobber/houseRobber.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/houseRobber/houseRobber.java
@@ -1,70 +1,73 @@
package com.phasmidsoftware.dsaipg.graphs.dynamicProgramming.houseRobber;
-import com.phasmidsoftware.dsaipg.graphs.dynamicProgramming.knapsack.Graph;
+import com.phasmidsoftware.dsaipg.graphs.dag.DiGraph;
+import com.phasmidsoftware.dsaipg.graphs.dag.Edge;
import com.phasmidsoftware.dsaipg.graphs.dynamicProgramming.knapsack.Vertex;
import static com.phasmidsoftware.dsaipg.graphs.dynamicProgramming.knapsack.BellmanFord.bellmanFordAlgorithm;
/**
- * The {@code houseRobber} class provides methods to solve the problem of
- * maximizing the profit a robber can achieve by selecting specific houses
- * while adhering to constraints that no two consecutive houses can be robbed.
+ * The house-robber problem, solved as a shortest path.
*
- * This implementation uses graph construction to represent all possible
- * combinations of houses that can be robbed and the Bellman-Ford algorithm
- * to find the optimal solution.
+ * Given the values of a row of houses, take the largest total that does not
+ * include two adjacent houses. The graph is the decision tree: at each house the
+ * path either skips it, at no cost, or takes it, at a cost of MINUS its value —
+ * so the shortest path through the tree is the most valuable selection, and
+ * Bellman-Ford finds it because it tolerates negative weights.
+ *
+ * A vertex's id records the decisions made so far as a string of 0s and 1s, and
+ * the rule that no two houses may be adjacent is enforced by refusing to append a
+ * 1 to an id that already ends in one.
+ *
+ * NOTE this used to build a Graph declared in the knapsack package — a fourth
+ * graph implementation in this tree. It uses {@link DiGraph} now.
*
- * @author Chandan Anandachari
+ * @author Chandan Anandachari (original, with its own Graph)
*/
public class houseRobber {
+
/**
- * Method to return the Maximum profit after selecting the houses.
- *
- * @param houseValue the profit which a robber can get from different houses.
- * @return Maximum profit robber can rob items from different house.
+ * @param houseValue the value of each house, in a row.
+ * @return the largest total obtainable without robbing two adjacent houses.
*/
public static double solveHouseRobber(double[] houseValue) {
- //Source Vertex with id:0
Vertex source = new Vertex("0", 0);
- Graph graph = new Graph(source);
- graph.addVertex(source);
- //Destination Vertex with id:9999
Vertex target = new Vertex("9999", 0);
+ DiGraph graph = new DiGraph<>();
+ graph.addVertex(source);
graph.addVertex(target);
-
- Graph graph1 = buildTheGraph(graph, houseValue, source, target, 0);
-
- return (Math.abs(bellmanFordAlgorithm(graph, source, target)));
+ buildTheGraph(graph, houseValue, source, target, 0);
+ // NOTE abs, because the edge weights are negated so that "shortest" means
+ // "most valuable".
+ return Math.abs(bellmanFordAlgorithm(graph, source, target));
}
/**
- * Method to return the graph which has all the edges and vertices.
+ * Build the decision tree below source, recursively.
*
- * @param graph Graph object.
- * @param source source vertex
- * @param target target vertex
- * @param houseValues the profit which a robber can get from different houses.
- * @param counter has the index position
- * @return Graph which will have all the possibilities a robber can rob a house .
+ * @param graph the graph to add to.
+ * @param houseValues the values.
+ * @param source the decision reached so far.
+ * @param target the vertex every complete decision leads to.
+ * @param counter which house is being decided.
+ * @return the graph, for convenience.
*/
- public static Graph buildTheGraph(Graph graph, double[] houseValues, Vertex source, Vertex target, int counter) {
+ public static DiGraph buildTheGraph(DiGraph graph, double[] houseValues,
+ Vertex source, Vertex target, int counter) {
if (counter >= houseValues.length) {
- graph.addEdge(source, target, 0);
+ graph.addEdge(new Edge<>(source, target, 0.0));
return graph;
}
-
- Vertex vertex1 = new Vertex(source.getId() + "0", source.getCurrentBagWeight());
- graph.addVertex(vertex1);
- graph.addEdge(source, vertex1, 0);
- buildTheGraph(graph, houseValues, vertex1, target, counter + 1);
-
- if (((source.getId()).charAt((source.getId()).length() - 1)) != '1') {
- Vertex vertex2 = new Vertex(source.getId() + "1", source.getCurrentBagWeight() + houseValues[counter]);
- graph.addVertex(vertex2);
- graph.addEdge(source, vertex2, -houseValues[counter]);
- buildTheGraph(graph, houseValues, vertex2, target, counter + 1);
+ // skip this house: no gain, no constraint
+ Vertex skip = new Vertex(source.getId() + "0", source.getCurrentBagWeight());
+ graph.addEdge(new Edge<>(source, skip, 0.0));
+ buildTheGraph(graph, houseValues, skip, target, counter + 1);
+ // take it, unless the previous house was taken
+ if (source.getId().charAt(source.getId().length() - 1) != '1') {
+ Vertex take = new Vertex(source.getId() + "1", source.getCurrentBagWeight() + houseValues[counter]);
+ graph.addEdge(new Edge<>(source, take, -houseValues[counter]));
+ buildTheGraph(graph, houseValues, take, target, counter + 1);
}
-
return graph;
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/BellmanFord.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/BellmanFord.java
index 8008f4de..80b7fb35 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/BellmanFord.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/BellmanFord.java
@@ -1,60 +1,71 @@
package com.phasmidsoftware.dsaipg.graphs.dynamicProgramming.knapsack;
+import com.phasmidsoftware.dsaipg.graphs.dag.DiGraph;
+import com.phasmidsoftware.dsaipg.graphs.dag.Edge;
+
+import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
/**
- * The BellmanFord class provides a method to implement the Bellman-Ford algorithm
- * that calculates the shortest path in a graph from a source vertex to a target vertex.
- * It handles graphs with weighted edges, including those with negative weights.
+ * The Bellman-Ford single-source shortest-path algorithm.
+ *
+ * Relax every edge, |V| - 1 times. After k passes every shortest path of k edges
+ * or fewer has been found, and no shortest path in a graph without negative cycles
+ * uses more than |V| - 1 edges, so that many passes suffice. Unlike Dijkstra it
+ * copes with negative weights, which is why it is here: the knapsack formulation
+ * gives an edge a NEGATIVE weight for the value gained, so that the shortest path
+ * is the most valuable packing.
+ *
+ * NOTE this used to run on a Graph, Edge and Vertex declared in this package — a
+ * fourth graph implementation in a tree that already had three. It runs on
+ * {@link DiGraph} now, which models it exactly: a vertex is a {@link Vertex} and
+ * an edge attribute is the weight. Removing the local classes also removed four
+ * defects that came with them, listed in the notes on this package in
+ * `Deferred work.md`.
*
- * @author Chandan Anandachari
+ * @author Chandan Anandachari (original, with its own Graph)
*/
public class BellmanFord {
+
/**
- * Method to return the Maximum profit after selecting particular items using Bellman Ford Algorithm.
+ * Find the length of the shortest path from source to target.
*
- * @param graph the weights for each item.
- * @param source source Vertex.
- * @param target target Vertex.
- * @return Maximum profit after selecting items from the list of items available.
+ * @param graph the graph, whose edge attributes are the weights.
+ * @param source the vertex to start from.
+ * @param target the vertex to reach.
+ * @return the total weight of the shortest path.
+ * @throws IllegalArgumentException if target cannot be reached from source.
*/
- public static double bellmanFordAlgorithm(Graph graph, Vertex source, Vertex target) {
-
- int V = graph.V(), E = graph.E();
-
- HashMap shortestDistance = new HashMap<>();
-
+ public static double bellmanFordAlgorithm(DiGraph graph, Vertex source, Vertex target) {
+ Map shortestDistance = new HashMap<>();
shortestDistance.put(source, 0.0);
- HashMap> hm = graph.getAdjacent();
- List allEdges = new LinkedList<>();
+ // NOTE gathered once. The original rebuilt this list on every call and
+ // filtered it with allEdges.contains(edge) -- which never removed anything,
+ // because Edge had no equals and so compared by identity, and which cost
+ // O(E^2) to achieve that.
+ List> allEdges = new ArrayList<>();
+ for (Edge edge : graph.edges()) allEdges.add(edge);
- for (Vertex vertex : hm.keySet()) {
- Iterator it = graph.edges(vertex);
- while (it.hasNext()) {
- Edge edge = it.next();
- if (!allEdges.contains(edge))
- allEdges.add(edge);
+ int v = graph.vertices().size();
+ for (int i = 1; i < v; i++)
+ for (Edge edge : allEdges) {
+ Double fromDistance = shortestDistance.get(edge.getFrom());
+ if (fromDistance == null) continue; // not yet reached
+ double candidate = fromDistance + edge.getAttributes();
+ Double toDistance = shortestDistance.get(edge.getTo());
+ if (toDistance == null || candidate < toDistance)
+ shortestDistance.put(edge.getTo(), candidate);
}
- }
-
- for (int i = 1; i < V; ++i) {
- for (int j = 0; j < E; ++j) {
- Vertex v1 = allEdges.get(j).source();//u
- Vertex v2 = allEdges.get(j).destination();//v
- double weight = allEdges.get(j).getEdgeWeight();
-
- if (shortestDistance.getOrDefault(v1, Double.MAX_VALUE) != Double.MAX_VALUE && (shortestDistance.get(v1) + weight) < shortestDistance.getOrDefault(v2, Double.MAX_VALUE)) {
- shortestDistance.put(v2, shortestDistance.get(v1) + weight);
- }
- }
- }
-
- // Negating the shortest Distance
- return (shortestDistance.get(target));
+ Double result = shortestDistance.get(target);
+ // NOTE the original returned the map lookup directly, so an unreachable
+ // target gave a NullPointerException on unboxing -- from a method declared
+ // to return a double, which gives the caller nothing to work with.
+ if (result == null)
+ throw new IllegalArgumentException("bellmanFordAlgorithm: " + target + " is not reachable from " + source);
+ return result;
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Coins.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Coins.java
index fd442471..f1afe12a 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Coins.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Coins.java
@@ -3,25 +3,55 @@
import com.google.common.collect.ImmutableList;
import com.phasmidsoftware.dsaipg.adt.bqs.Dictionary;
import com.phasmidsoftware.dsaipg.adt.bqs.Dictionary_Hash;
+import com.phasmidsoftware.dsaipg.graphs.dag.DiGraph;
+import com.phasmidsoftware.dsaipg.graphs.dag.Edge;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
- * ANother class (!) to implement the Coin-change problem using dynamic programming.
+ * The coin chooser: the fewest coins making a given value, by dynamic programming
+ * over an explicit dependency graph.
+ *
+ * This is Figure 10.11 of the book. A vertex is a remaining value — a sub-problem,
+ * "how few coins make x" — and an edge from x to x - c records the choice of one
+ * coin of denomination c. Every edge therefore costs exactly one coin, so the
+ * fewest coins making v is the shortest path from v to 0.
+ *
+ * The recurrence the graph expresses:
+ *
+ * m(0) = 0
+ * m(x) = min over i of { m(x - c[i]) : x >= c[i] } + 1
+ *
+ * Because every edge goes to a strictly smaller value, the graph is acyclic — the
+ * book notes there is "no complication due to cycles" — and descending value order
+ * is a topological order, so a single memoised pass suffices. Time and space are
+ * both Theta(v), as the book says.
+ *
+ * NOTE the graph is built rather than left implicit in the recursion, so that the
+ * structure the book draws is something a caller can inspect and a test can assert
+ * about; see {@link #getGraph}. An edge exists only where the coin fits, so no
+ * sub-problem is visited for a denomination too large to use.
+ *
+ * NOTE the book presents this as an improvement on "applying the Bellman-Ford
+ * algorithm naively", which is O(vw). The improvement is not in avoiding the graph
+ * but in walking it once, in an order that needs no relaxation.
*/
public class Coins {
/**
- * Inner class to represent a (sub) solution of the Coins problem.
+ * A count of each denomination, and the total number of coins.
*/
class Solution implements Comparable {
-
public int compareTo(Solution o) {
return Integer.compare(n, o.n);
}
+ /**
+ * @param i the index of a denomination to add one of.
+ * @return a new Solution with one more coin of that denomination.
+ */
public Solution increment(int i) {
if (n == Integer.MAX_VALUE) return this;
int[] copied = Arrays.copyOf(counts, counts.length);
@@ -68,13 +98,7 @@ public String toString() {
'}';
}
- /**
- * The number of coins in this Solution.
- */
final int n;
- /**
- * The numbers of each denomination in this Solution.
- */
final int[] counts;
public Solution(int n, int[] counts) {
@@ -84,34 +108,68 @@ public Solution(int n, int[] counts) {
}
/**
- * Method to get the minimum number of coins required to total amount.
- *
- * @param amount the amount to be given in change.
- * @return the minimum number of coins required.
+ * @param amount the value to make.
+ * @return the Solution using the fewest coins.
*/
Solution number(int amount) {
return mu(amount);
}
+ /**
+ * @return how many sub-problems have been solved and remembered.
+ */
int subProblems() {
return memo.size();
}
/**
- * Recursive (private) method. It's package-protected here to make it easy to test.
+ * The dependency graph, as far as it has been built.
+ *
+ * A vertex is a remaining value; an edge from x to y is attributed with the
+ * INDEX of the denomination whose use takes x to y. Exposed because it is the
+ * structure the book draws, and being able to look at it is the point of
+ * building it.
+ *
+ * @return the graph.
+ */
+ DiGraph getGraph() {
+ return graph;
+ }
+
+ /**
+ * Extend the graph to cover every value reachable from amount.
+ *
+ * @param amount the value to make.
+ */
+ private void buildGraph(int amount) {
+ for (int x = built + 1; x <= amount; x++)
+ for (int i = 0; i < coins.size(); i++)
+ if (coins.get(i) <= x)
+ graph.addEdge(new Edge<>(x, x - coins.get(i), i));
+ if (amount > built) built = amount;
+ }
+
+ /**
+ * Solve for one value, using the memoized solutions of its dependencies.
*
- * @param amount the amount of change required.
- * @return the minimum number of coins which can represent the amount.
+ * @param amount the value to make.
+ * @return the best Solution for it.
*/
Solution mu(int amount) {
Solution result = memo.get(amount);
if (result != null) return result;
if (amount < 0) return nullSolution;
if (amount == 0) return zero;
- Solution[] subsolutions = new Solution[coins.size()];
- for (int i = 0; i < subsolutions.length; i++) subsolutions[i] = mu(amount - coins.get(i)).increment(i);
+ // NOTE mu extends the graph itself, so it still stands alone -- CoinsTest
+ // calls it directly, without going through number().
+ buildGraph(amount);
result = nullSolution;
- for (Solution option : subsolutions) if (result.compareTo(option) > 0) result = option;
+ // NOTE the options come from the graph. Each outgoing edge is one coin
+ // spent, and its attribute says which denomination that was.
+ for (Edge edge : graph.adjacent(amount)) {
+ Solution option = mu(edge.getTo()).increment(edge.getAttributes());
+ if (result.compareTo(option) > 0) result = option;
+ }
result.validate();
memo.put(amount, result);
return result;
@@ -119,6 +177,11 @@ Solution mu(int amount) {
public Coins(List coins) {
this.coins = coins;
+ // NOTE built here, not at the field, because zeros() now depends on coins
+ // and a field initialiser runs before the constructor body. The old zeros()
+ // returned a hard-coded four-element array, so it needed nothing.
+ this.zero = new Solution(0, zeros());
+ this.nullSolution = new Solution(Integer.MAX_VALUE, zeros());
}
public Coins() {
@@ -126,22 +189,23 @@ public Coins() {
}
/**
- * CONSIDER adding a parameter for the number of denominations.
- *
- * @return an array of all zeros.
+ * @return a count of zero for each denomination.
+ *
+ * NOTE one entry per denomination, however many there are. A fixed length
+ * would give an ArrayIndexOutOfBoundsException from Solution.increment for any
+ * coin list of a different size.
*/
int[] zeros() {
- return new int[]{0, 0, 0, 0};
+ return new int[coins.size()];
}
- final Solution zero = new Solution(0, zeros());
-
final static List US = ImmutableList.of(1, 5, 10, 25);
-
- private final Solution nullSolution = new Solution(Integer.MAX_VALUE, zeros());
private final List coins;
-
+ final Solution zero;
+ private final Solution nullSolution;
+ private final DiGraph graph = new DiGraph<>();
+ /** the largest value the graph has been built out to */
+ private int built = 0;
// The following is to memoize the sub-solutions: key is of type Key, and value is value (an Integer).
private final Dictionary memo = new Dictionary_Hash<>();
-
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Edge.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Edge.java
deleted file mode 100644
index d62ec96a..00000000
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Edge.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.phasmidsoftware.dsaipg.graphs.dynamicProgramming.knapsack;
-
-/**
- * The {@code Edge} class represents an edge in a graph.
- * An edge connects two vertices (source and destination)
- * and has an associated weight which typically represents
- * a cost, distance, or capacity.
- *
- * @author Chandan Anandachari
- */
-public class Edge {
- private final Vertex U;
- private final Vertex V;
- private final double edgeWeight;
-
- /**
- * Initializes an edge between vertices {@code U} and {@code V} of
- * the given {@code edgeWeight}.
- *
- * @param U source vertex
- * @param V destination vertex
- * @param edgeWeight the weight of this edge
- */
- public Edge(Vertex U, Vertex V, double edgeWeight) {
- this.U = U;
- this.V = V;
- this.edgeWeight = edgeWeight;
- }
-
- /**
- * Returns the weight of this edge.
- *
- * @return the edgeWeight of this edge
- */
- public double getEdgeWeight() {
- return this.edgeWeight;
- }
-
- /**
- * Returns source endpoint of this edge.
- *
- * @return source endpoint of this edge
- */
- public Vertex source() {
- return this.U;
- }
-
- /**
- * Returns destination endpoint of this edge.
- *
- * @return destination endpoint of this edge
- */
- public Vertex destination() {
- return this.V;
- }
-
-}
\ No newline at end of file
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Graph.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Graph.java
deleted file mode 100644
index 589efd79..00000000
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Graph.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.phasmidsoftware.dsaipg.graphs.dynamicProgramming.knapsack;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-
-/**
- * The {@code Graph} class represents a directed graph.
- * It supports adding vertices, adding edges between vertices,
- * and provides access to various graph properties such as the number
- * of vertices, the number of edges, and adjacency relationships.
- *
- * The graph is represented using an adjacency list, where each
- * vertex is associated with a list of its adjacent edges.
- *
- * @author Chandan Anandachari
- */
-public class Graph {
-
- private final HashMap> adjacent;
- private final Vertex root;
- private int V;
- private int E;
-
- public Vertex getVertex() {
- return root;
- }
-
- /**
- * Initializes the root vertex of the graph and also adjacent list.
- *
- * @param root source vertex
- */
- public Graph(Vertex root) {
- this.V = 0;
- this.E = 0;
- this.root = root;
- adjacent = new HashMap<>();
- }
-
- /**
- * Adds a new Vertex into the adjacency list of vertices.
- */
- public void addVertex(Vertex v) {
- V++;
- adjacent.put(v, new LinkedList<>());
- }
-
- /**
- * Adds entry to the given Vertex as an adjacent vertex.
- */
- public void addEdge(Vertex u, Vertex v, double edgeWeight) {
- E++;
- adjacent.getOrDefault(u, new LinkedList<>()).add(new Edge(u, v, edgeWeight));
- }
-
- /**
- * Returns the directed edges incident from vertex {@code v}.
- *
- * @param v the vertex
- * @return the directed edges incident from vertex {@code v} as an Iterable
- */
- public Iterator edges(Vertex v) {
- if (adjacent.get(v) != null)
- return adjacent.get(v).iterator();
- else
- return null;
- }
-
- /**
- * Returns the number of vertices in this graph.
- *
- * @return the number of vertices in this graph
- */
- public int V() {
- return V;
- }
-
- /**
- * Yields the number of edges in this graph.
- *
- * @return the number of edges in this graph
- */
- public int E() {
- return E;
- }
-
- /**
- * Yields a List of adjacent edges of a particular vertex in this graph.
- *
- * @return a HashMap of adjacent edges of a particular vertex in this graph
- */
- public HashMap> getAdjacent() {
- return adjacent;
- }
-}
\ No newline at end of file
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Knapsack.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Knapsack.java
index cf6a441d..db84bd2e 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Knapsack.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Knapsack.java
@@ -3,13 +3,45 @@
import com.google.common.collect.ImmutableList;
import com.phasmidsoftware.dsaipg.adt.bqs.Dictionary;
import com.phasmidsoftware.dsaipg.adt.bqs.Dictionary_Hash;
+import com.phasmidsoftware.dsaipg.graphs.dag.DiGraph;
+import com.phasmidsoftware.dsaipg.graphs.dag.Edge;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
/**
- * Class to implement 0-1 Knapsack problem using dynamic programming.
+ * Class to implement the 0-1 Knapsack problem using dynamic programming, expressed
+ * as a walk over an explicit graph of sub-problems.
+ *
+ * A vertex is a sub-problem: the {@link Key} (kappa, omega) asks for the greatest
+ * value obtainable from the first kappa items within a weight of
+ * omega. An edge is a decision about item number kappa,
+ * and it carries as its attribute the item that decision packs -- or
+ * null where the decision is to leave that item behind. So the two
+ * recursive cases given in the book become the two edges out of a vertex:
+ *
+ * leave item kappa: an edge to (kappa - 1, omega) with no item attached;
+ * take item kappa: an edge to (kappa - 1, omega - w_kappa) attributed with
+ * the item -- present only when the item actually fits.
+ *
+ * The value of a packing is the sum of the values on the path, so the answer is the
+ * longest path from (n, W) to a base case. Contrast {@link Coins}, whose
+ * edges each cost exactly one coin and whose answer is therefore a shortest path.
+ *
+ * Every edge decreases kappa by exactly one, so the graph is acyclic however the
+ * weights fall, and the recursion terminates at kappa = 0.
+ *
+ * NOTE the edges out of a vertex are built as that vertex is first visited, not in
+ * advance. This is the point the book makes about the bottom-up method: solving
+ * naively "will evaluate all nW values of m, including many that will never be
+ * needed". Building lazily means the graph holds exactly the sub-problems the
+ * search reached -- for the Google data set, 31373 of a possible 42500.
+ *
+ * NOTE also that this walk does not depend on the order in which the adjacency bag
+ * offers the two edges, which matters because a Bag_Array iterates in a random
+ * order. The two decisions are told apart by whether an edge carries an item, and
+ * a tie between them is settled in favour of leaving the item behind.
*/
public class Knapsack {
@@ -32,6 +64,18 @@ int subProblems() {
return memo.size();
}
+ /**
+ * The graph of sub-problems, as far as it has been explored.
+ *
+ * It is empty until the first call of {@link #value} or {@link #mu}, and it
+ * grows with each call that reaches a sub-problem not seen before.
+ *
+ * @return the dependency graph.
+ */
+ DiGraph graph() {
+ return graph;
+ }
+
/**
* Recursive (private) method. It's package-protected here to make it easy to test.
*
@@ -45,22 +89,42 @@ Solution mu(int kappa, int omega) {
Solution value = memo.get(key);
if (value != null) return value;
if (kappa < 1) return empty;
- Solution r = mu(kappa - 1, omega);
- Item item = items.get(kappa - 1);
- if (item.weight > omega) value = r;
- else {
- Solution l = mu(kappa - 1, omega - item.weight).increment(item);
- value = l.compareTo(r) > 0 ? l : r;
+ addEdges(key);
+ Solution leave = empty, take = null;
+ for (Edge edge : graph.adjacent(key)) {
+ Key to = edge.getTo();
+ Item item = edge.getAttributes();
+ if (item == null) leave = mu(to.kappa, to.omega);
+ else take = mu(to.kappa, to.omega).increment(item);
}
+ value = take != null && take.compareTo(leave) > 0 ? take : leave;
memo.put(key, value);
return value;
}
+ /**
+ * Add the edges leading out of a sub-problem: the decision to leave item kappa
+ * behind, and -- if it fits -- the decision to pack it.
+ *
+ * Called exactly once per sub-problem, because {@link #mu} memoizes the result
+ * of every vertex whose edges it builds and returns early thereafter.
+ *
+ * @param key the sub-problem, whose kappa is at least 1.
+ */
+ private void addEdges(Key key) {
+ Item item = items.get(key.kappa - 1);
+ graph.addEdge(new Edge<>(key, new Key(key.kappa - 1, key.omega), null));
+ if (item.weight <= key.omega)
+ graph.addEdge(new Edge<>(key, new Key(key.kappa - 1, key.omega - item.weight), item));
+ }
+
public Knapsack(List- items) {
this.items = items;
}
private final List
- items;
+ // The graph of sub-problems and the decisions that connect them (see the class comment).
+ private final DiGraph
graph = new DiGraph<>();
// The following is to memoize the sub-solutions: key is of type Key, and value is value (an Integer).
private final Dictionary memo = new Dictionary_Hash<>();
final static Solution empty = new Solution(0, ImmutableList.of());
@@ -75,7 +139,6 @@ public int compareTo(Solution o) {
}
public Solution increment(Item item) {
- if (value == -1) return this;
List- list = Stream.concat(items.stream(), Stream.of(item)).toList();
return new Solution(value + item.value, list);
}
@@ -118,6 +181,10 @@ public static Solution of(Item item) {
/**
* Inner class
Key which represents the parameters of a subproblem (in this case, evaluating mu).
+ *
+ * It serves both as the key under which a sub-solution is memoized and as a
+ * vertex of the dependency graph -- these are the same thing, which is why the
+ * book recommends a hash table with "the key from k and w".
*/
static class Key {
@Override
@@ -142,8 +209,8 @@ public Key(int kappa, int omega) {
this.omega = omega;
}
- private final int kappa;
- private final int omega;
+ final int kappa;
+ final int omega;
}
public static class Item {
@@ -162,4 +229,4 @@ public Item(String id, int weight, int value) {
final int weight;
final int value;
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Vertex.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Vertex.java
index 3007d760..473729d8 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Vertex.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/knapsack/Vertex.java
@@ -52,4 +52,4 @@ public double getCurrentBagWeight() {
return this.currentBagWeight;
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/lucas/Fibonacci.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/lucas/Fibonacci.java
index bb6a428a..e40a53a4 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/lucas/Fibonacci.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/lucas/Fibonacci.java
@@ -1,46 +1,38 @@
package com.phasmidsoftware.dsaipg.graphs.dynamicProgramming.lucas;
+import java.math.BigInteger;
import java.util.ArrayList;
+import java.util.List;
/**
- * The Fibonacci class provides a way to compute the Fibonacci sequence.
- * The Fibonacci sequence is a series of numbers where each number is the sum of the two preceding ones,
- * traditionally starting with 1 and 1.
+ * The Fibonacci numbers, memoised: 1, 1, 2, 3, 5, 8, ...
*
- * This implementation uses memoization to optimize the computation by storing previously calculated results
- * in an internal list.
+ * NOTE these are BigIntegers, so there is no ceiling. An int would overflow at
+ * n = 47: fib(47) is 4,807,526,976 against an int maximum of 2,147,483,647. The
+ * Python port needs nothing special, its integers being arbitrary precision.
*/
public class Fibonacci {
/**
- * Retrieves the nth Fibonacci number.
- * If the value is already computed and stored, it is returned directly.
- * Otherwise, the value is calculated, memoized, and returned.
- *
- * @param n The index of the Fibonacci number to retrieve. Must be non-negative.
- * @return The nth Fibonacci number.
- * @throws UnsupportedOperationException If n is negative.
+ * @param n which term.
+ * @return the nth Fibonacci number.
+ * @throws UnsupportedOperationException if n is negative.
*/
- public int get(int n) {
+ public BigInteger get(int n) {
if (n < 0) throw new UnsupportedOperationException("Fibonacci.get is not supported for negative n");
if (n < fib.size()) return fib.get(n);
return evaluate(n);
}
- /**
- * Constructs a new Fibonacci object and initializes the base cases of the Fibonacci sequence.
- * The Fibonacci sequence always starts with the first two numbers as 1 and 1.
- * These values are precomputed and stored in the internal list for subsequent calculations.
- */
public Fibonacci() {
- fib.add(0, 1);
- fib.add(1, 1);
+ fib.add(0, BigInteger.ONE);
+ fib.add(1, BigInteger.ONE);
}
- private int evaluate(int n) {
- for (int i = fib.size(); i <= n; i++) fib.add(i, fib.get(i - 2) + fib.get(i - 1));
+ private BigInteger evaluate(int n) {
+ for (int i = fib.size(); i <= n; i++) fib.add(i, fib.get(i - 2).add(fib.get(i - 1)));
return fib.get(n);
}
- final ArrayList fib = new ArrayList<>();
-}
\ No newline at end of file
+ final List fib = new ArrayList<>();
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/lucas/Lucas.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/lucas/Lucas.java
index 05b88975..ae51ed09 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/lucas/Lucas.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/lucas/Lucas.java
@@ -1,35 +1,55 @@
package com.phasmidsoftware.dsaipg.graphs.dynamicProgramming.lucas;
+import java.math.BigInteger;
import java.util.ArrayList;
+import java.util.List;
/**
- * The Lucas class represents a numerical sequence called the Lucas series, which is similar to the Fibonacci sequence
- * but starts with different initial values. The sequence starts with 2 and 1, and each subsequent term is calculated
- * as the sum of the two preceding terms.
+ * The Lucas numbers, memoised: 2, 1, 3, 4, 7, 11, ...
+ *
+ * The same recurrence as Fibonacci, seeded 2 and 1.
+ *
+ * NOTE BigInteger rather than long, for the reason given in Fibonacci: these
+ * sequences exist to grow, and a silent ceiling is the wrong behaviour for them.
*/
public class Lucas {
- public Lucas() {
- lucas.add(0, 2L);
- lucas.add(1, 1L);
- }
- public long get(int n) {
+ /**
+ * @param n which term.
+ * @return the nth Lucas number, computed once and remembered.
+ * @throws UnsupportedOperationException if n is negative.
+ */
+ public BigInteger get(int n) {
if (n < 0) throw new UnsupportedOperationException("Lucas.get is not supported for negative n");
if (n < lucas.size()) return lucas.get(n);
return evaluate(n);
}
- public long bad(int n) {
+ /**
+ * The same sequence by naive recursion, which recomputes both predecessors
+ * every time and so costs exponentially. Kept because contrasting it with
+ * {@link #get} is the point of this package.
+ *
+ * @param n which term.
+ * @return the nth Lucas number.
+ * @throws UnsupportedOperationException if n is negative.
+ */
+ public BigInteger bad(int n) {
if (n < 0) throw new UnsupportedOperationException("Lucas.get is not supported for negative n");
- if (n == 0) return 2L;
- if (n == 1) return 1L;
- return bad(n - 2) + bad(n - 1);
+ if (n == 0) return BigInteger.TWO;
+ if (n == 1) return BigInteger.ONE;
+ return bad(n - 2).add(bad(n - 1));
+ }
+
+ public Lucas() {
+ lucas.add(0, BigInteger.TWO);
+ lucas.add(1, BigInteger.ONE);
}
- private long evaluate(int n) {
- for (int i = lucas.size(); i <= n; i++) lucas.add(i, lucas.get(i - 2) + lucas.get(i - 1));
+ private BigInteger evaluate(int n) {
+ for (int i = lucas.size(); i <= n; i++) lucas.add(i, lucas.get(i - 2).add(lucas.get(i - 1)));
return lucas.get(n);
}
- final ArrayList lucas = new ArrayList<>();
-}
\ No newline at end of file
+ final List lucas = new ArrayList<>();
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/lucas/Pell.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/lucas/Pell.java
index 2c661129..60944e23 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/lucas/Pell.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/dynamicProgramming/lucas/Pell.java
@@ -1,46 +1,43 @@
package com.phasmidsoftware.dsaipg.graphs.dynamicProgramming.lucas;
+import java.math.BigInteger;
import java.util.ArrayList;
+import java.util.List;
/**
- * The Pell class represents a numerical sequence called the Pell sequence.
- * The Pell numbers are a series of integers where each number is defined by the recurrence relation:
- * P(n) = 2 * P(n-1) + P(n-2), with the initial values P(0) = 0 and P(1) = 1.
+ * The Pell numbers, memoised: 0, 1, 2, 5, 12, 29, ...
*
- * This implementation uses memoization to compute Pell numbers efficiently, storing results
- * in an internal list to avoid redundant calculations.
+ * x[i] = x[i-2] + 2 * x[i-1].
+ *
+ * NOTE BigInteger, and here it was not merely theoretical. These grow by a factor
+ * of about 2.414 each term, so they pass a 64-bit long at around n = 62 — and
+ * PellTest asserted {@code get(90) == 7052354271195710746}, which is not the 90th
+ * Pell number but what a long holds once that value has wrapped. The true value is
+ * 9,960,168,529,794,442,859,224,531,878,561,050, which the test now asserts.
*/
public class Pell {
/**
- * Retrieves the nth Pell number.
- * If the value is already computed and stored in the internal list, it is returned directly.
- * Otherwise, the value is calculated using the recurrence relation, memoized, and returned.
- *
- * @param n The index of the Pell number to retrieve. Must be non-negative.
- * @return The nth Pell number.
- * @throws UnsupportedOperationException If n is negative.
+ * @param n which term.
+ * @return the nth Pell number, computed once and remembered.
+ * @throws UnsupportedOperationException if n is negative.
*/
- public long get(int n) {
+ public BigInteger get(int n) {
if (n < 0) throw new UnsupportedOperationException("Pell.get is not supported for negative n");
if (n < pell.size()) return pell.get(n);
return evaluate(n);
}
- /**
- * Constructs a new Pell object and initializes the base cases of the Pell sequence.
- * The Pell sequence begins with the first two numbers P(0) = 0 and P(1) = 1.
- * These values are precomputed and stored in the internal list for subsequent calculations.
- */
public Pell() {
- pell.add(0, 0L);
- pell.add(1, 1L);
+ pell.add(0, BigInteger.ZERO);
+ pell.add(1, BigInteger.ONE);
}
- private long evaluate(int n) {
- for (int i = pell.size(); i <= n; i++) pell.add(i, pell.get(i - 2) + 2 * pell.get(i - 1));
+ private BigInteger evaluate(int n) {
+ for (int i = pell.size(); i <= n; i++)
+ pell.add(i, pell.get(i - 2).add(BigInteger.TWO.multiply(pell.get(i - 1))));
return pell.get(n);
}
- final ArrayList pell = new ArrayList<>();
-}
\ No newline at end of file
+ final List pell = new ArrayList<>();
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/Edge.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/Edge.java
index 0a181f94..202cd950 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/Edge.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/Edge.java
@@ -100,4 +100,4 @@ public String toString() {
}
-/*Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
\ No newline at end of file
+/*Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/EdgeWeightedGraph.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/EdgeWeightedGraph.java
index ec029e93..7a62bb33 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/EdgeWeightedGraph.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/EdgeWeightedGraph.java
@@ -213,4 +213,4 @@ public String toString() {
}
-/*Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
\ No newline at end of file
+/*Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/GBFS.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/GBFS.java
index 66d7fae4..ec7e5300 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/GBFS.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/GBFS.java
@@ -41,4 +41,4 @@ public List bfs(Graph G, T s) {
}
return output;
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/GBag.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/GBag.java
index 788d3b91..13c76092 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/GBag.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/GBag.java
@@ -108,3 +108,4 @@ public T next() {
/*Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
+
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/Graph.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/Graph.java
index fe991a4d..548c3386 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/Graph.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/Graph.java
@@ -41,4 +41,4 @@ public void addEdge(T v, T w) {
public Iterator adj(T v) {
return adj.get(v).iterator();
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/MinPQ.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/MinPQ.java
index c1fac2ad..55fd56b4 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/MinPQ.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/MinPQ.java
@@ -264,4 +264,4 @@ public Key next() {
}
-/*Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
\ No newline at end of file
+/*Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/Prims.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/Prims.java
index 3dd0fc4c..96a90970 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/Prims.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/Prims.java
@@ -40,4 +40,4 @@ private void visit(EdgeWeightedGraph G, int v) { // Mark v and add to pq all edg
public Iterable edges() {
return mst;
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/QueueG.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/QueueG.java
index 3b36e47d..93279aa1 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/QueueG.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/QueueG.java
@@ -57,4 +57,4 @@ public T peek() {
}
return arr[front];
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/StdRandom.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/StdRandom.java
index 928d070c..ba847ea1 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/StdRandom.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/StdRandom.java
@@ -575,4 +575,4 @@ private static void validateSubarrayIndices(int from, int to, int length) {
/*
-Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
\ No newline at end of file
+Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/package-info.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/package-info.java
index 6808eb6a..9cce3036 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/package-info.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/generic_BFS_and_prims/package-info.java
@@ -1,3 +1,3 @@
package com.phasmidsoftware.dsaipg.graphs.generic_BFS_and_prims;
-// This package needs work!
\ No newline at end of file
+// This package needs work!
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/BaseGeoGraph.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/BaseGeoGraph.java
index dbdc414e..d4346106 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/BaseGeoGraph.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/BaseGeoGraph.java
@@ -39,4 +39,4 @@ public SizedIterable> geoEdges() {
return SizedIterableImpl.create(result);
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Boruvka.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Boruvka.java
index 0933e1d6..271bd2ed 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Boruvka.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Boruvka.java
@@ -74,6 +74,13 @@ public Boruvka(EdgeGraph graph) {
this.mst = new ArrayList<>(); // Initialize mst to an empty list.
try {
mst = runBoruvka();
+ } catch (com.phasmidsoftware.dsaipg.util.general.ImplementationMissing e) {
+ // An unwritten exercise must not be dressed up as an empty MST. Swallowing
+ // it here left the student tree reporting three tunnels tests as genuine
+ // failures -- "the MST is empty" -- when the real answer was that
+ // UF_HWQUPC.find has yet to be written. Rethrowing lets
+ // CancelOnNotImplemented report them as skipped, which is the truth.
+ throw e;
} catch (Exception e) {
e.printStackTrace(); // TODO log this
}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Geo.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Geo.java
index 30759306..c8d8a9fd 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Geo.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Geo.java
@@ -27,4 +27,4 @@ public interface Geo extends EdgeGraph {
* @return the length of the edge
*/
double length(Edge edge);
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoEdge.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoEdge.java
index e0fc77aa..244a2573 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoEdge.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoEdge.java
@@ -37,4 +37,4 @@ public static Edge create(Edge edge) {
V v = edge.get();
return new GeoEdge<>(v, edge.getOther(v), edge.getAttribute());
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoGraphSpherical.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoGraphSpherical.java
index 9e2d9121..d42914a5 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoGraphSpherical.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoGraphSpherical.java
@@ -46,4 +46,4 @@ private static double distance(Position p1, Position p2) {
}
private final static int R = 6378100; // Radius of the earth (meters)
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoKruskal.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoKruskal.java
index afa7c861..856cf6d5 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoKruskal.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoKruskal.java
@@ -59,4 +59,4 @@ public GeoKruskal(EdgeGraph graph) {
super(graph);
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoMST.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoMST.java
index b6935d39..31caaff4 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoMST.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoMST.java
@@ -2,4 +2,4 @@
public interface GeoMST & Sequenced> {
Geo getGeoMST(Geo geoGraph);
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoPoint.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoPoint.java
index f318c9fe..222bce54 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoPoint.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/GeoPoint.java
@@ -21,4 +21,4 @@ public interface GeoPoint {
* the geographic coordinates (x and y values) of the point.
*/
Position getPosition();
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Kml.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Kml.java
index b2dbf7eb..b2b59f82 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Kml.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Kml.java
@@ -162,3 +162,4 @@ private String asLine(Edge edge) {
"\n";
}
+
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Kruskal.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Kruskal.java
index d3f70b76..dc339655 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Kruskal.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Kruskal.java
@@ -85,6 +85,14 @@ public Kruskal(EdgeGraph graph) {
this.size = uf.size();
try {
mst = runKruskal();
+ } catch (com.phasmidsoftware.dsaipg.util.general.ImplementationMissing e) {
+ // An unwritten exercise must not be dressed up as an empty MST.
+ // Swallowing it here reported the tunnels tests as genuine failures --
+ // "the MST is empty" -- when the real answer is that UF_HWQUPC.find has
+ // yet to be written. Rethrowing lets CancelOnNotImplemented report them
+ // as skipped, which is the truth. Inert in this tree, where the
+ // exercises are written.
+ throw e;
} catch (Exception e) {
e.printStackTrace(); // TODO log this
}
@@ -171,4 +179,4 @@ private void showEdgesInSequence(EdgeGraph graph) {
private final PriorityQueue> pq;
private final TypedUF uf;
private final int size;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/MST.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/MST.java
index 72815489..3e522805 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/MST.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/MST.java
@@ -20,6 +20,11 @@
*/
public abstract class MST & Sequenced> implements Iterable> {
public MST() {
+ // NOTE an empty tree, not null. If a subclass constructor fails to assign
+ // this -- because an exercise it depends on is unwritten, say -- the
+ // inherited iterator() should report no edges rather than throw a
+ // NullPointerException on the way to explaining nothing.
+ this.mst = new ArrayList<>();
}
/**
@@ -48,4 +53,4 @@ public Iterator> iterator() {
protected Iterable> mst;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Position_Spherical.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Position_Spherical.java
index 8602524f..3231cd4e 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Position_Spherical.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Position_Spherical.java
@@ -85,4 +85,4 @@ public int hashCode() {
private final double latitude;
private final double longitude;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Prim.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Prim.java
index b27ab308..f5d7a56b 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Prim.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Prim.java
@@ -8,8 +8,6 @@
import com.phasmidsoftware.dsaipg.graphs.undirected.Edge;
import com.phasmidsoftware.dsaipg.graphs.undirected.EdgeGraph;
import com.phasmidsoftware.dsaipg.graphs.undirected.Graph_Edges;
-import com.phasmidsoftware.dsaipg.util.iteration.SizedIterable;
-import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -55,19 +53,6 @@ public EdgeGraph getMST() {
return result;
}
- /**
- * Returns an iterator over the edges in the Minimum Spanning Tree (MST) of the graph.
- * The edges are iterated in the order they were added to the MST.
- *
- * @return an iterator over the edges in the MST of type {@code Edge}.
- */
- @NotNull
- public Iterator> iterator() {
- Collection> result = new ArrayList<>();
- for (Edge edge : mst) result.add(edge);
- return result.iterator();
- }
-
/**
* Constructor to initialize the Prim object that calculates the Minimum Spanning Tree (MST)
* for an edge-weighted graph using Prim's algorithm.
@@ -83,9 +68,7 @@ public Prim(EdgeGraph graph) {
int size = graph.vertices().size();
marked = new boolean[size];
// TO BE IMPLEMENTED : finish construction
- this.pq = null;
- this.mst = null;
- // END SOLUTION
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -98,8 +81,7 @@ public Prim(EdgeGraph graph) {
*/
private Iterable> runPrim() throws PQException {
// TO BE IMPLEMENTED : finish construction
- throw new RuntimeException("implementation missing");
- // END SOLUTION
+ throw new com.phasmidsoftware.dsaipg.util.general.ImplementationMissing();
}
/**
@@ -122,7 +104,10 @@ private void prim(V v) throws PQException {
if (marked[ui] && marked[wi]) continue; // lazy, both v and w already scanned
queue.offer(e); // add e to queue
//weight += e.weight();
- if (!marked[ui]) scan(v); // v becomes part of tree
+ // NOTE scan the ENDPOINTS of e, u and w -- not v, which is merely the
+ // vertex this run of Prim started from. Scanning v again would re-offer
+ // its edges and leave u unscanned, and it trips scan's own assertion.
+ if (!marked[ui]) scan(u); // u becomes part of tree
if (!marked[wi]) scan(w); // w becomes part of tree
}
}
@@ -147,20 +132,18 @@ private void scan(V v) {
}
/**
- * Creates and returns a priority queue of edges based on a specified comparator for their attributes.
- * The priority queue is initialized with the given edges and configured as a min-priority queue.
- * This method is copied from Kruskal.java, needed for creating a priority queue of edges
- * those edges connecting the mst to the fringe vertices
+ * Creates an empty min-priority queue of edges, ordered by attribute, with room
+ * for the given number of edges.
+ *
+ * NOTE the capacity is not advisory. {@link PriorityQueue_BinaryHeap#give}
+ * silently drops an element when the heap is full rather than growing, so a
+ * queue built too small loses edges without saying so.
*
- * @param edges the collection of edges to be added to the priority queue.
- * The size of this collection defines the initial capacity of the priority queue.
- * @return a PriorityQueue of type {@code Edge} containing all the elements from the input collection,
- * ordered by their attributes in ascending order.
+ * @param capacity the greatest number of edges the queue will have to hold.
+ * @return an empty min-priority queue.
*/
- private PriorityQueue> createPQ(SizedIterable> edges) {
- PriorityQueue> result = new PriorityQueue_BinaryHeap<>(edges.size(), false, Comparator.comparing(Edge::getAttribute), false);
- for (Edge e : edges) result.give(e);
- return result;
+ private PriorityQueue> emptyPQ(int capacity) {
+ return new PriorityQueue_BinaryHeap<>(capacity, false, Comparator.comparing(Edge::getAttribute), false);
}
/**
@@ -178,21 +161,10 @@ private PriorityQueue> createPQ(SizedIterable> edges) {
* @param the attribute type for the edge, typically the weight or cost, which must be comparable.
*/
private final Queue> queue;
- /**
- * Represents an iterable collection of edges that form the Minimum Spanning Tree (MST)
- * of an edge-weighted graph. Each edge in the collection connects two vertices and
- * may have an associated attribute (e.g., weight).
- *
- * The MST is computed using Prim's algorithm and stored as an iterable object to allow
- * sequential access to the edges in the order they were added during the algorithm.
- * This attribute is initialized when the MST computation is executed and can be
- * accessed for traversal or analysis of the resulting tree structure.
- *
- * @param the vertex type of the edges in the MST
- * @param the attribute type of the edges in the MST; must be comparable to allow
- * sorting or prioritization (e.g., by weight)
- */
- private Iterable> mst;
+ // NOTE the MST itself lives in the superclass, as MST.mst, which MST.iterator()
+ // reads. Do not declare a field of that name here: it would shadow the
+ // superclass's, leaving MST.mst null and the inherited iterator() with nothing
+ // to report.
/**
* Priority queue of edges used by Prim's algorithm to construct the minimum spanning tree (MST).
* This priority queue maintains edges with one endpoint inside the spanning tree and the other outside,
@@ -233,4 +205,4 @@ private PriorityQueue> createPQ(SizedIterable> edges) {
*/
private final Map vertexToInteger;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Sequenced.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Sequenced.java
index 3a2c237c..27b6d1ad 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Sequenced.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/Sequenced.java
@@ -22,4 +22,4 @@ public interface Sequenced {
* used to order or uniquely identify the object implementing this method.
*/
void setSequence(int sequence);
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/ShortestPaths.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/ShortestPaths.java
index 1f016ebb..2e76c2d8 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/ShortestPaths.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/gis/ShortestPaths.java
@@ -5,8 +5,10 @@
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.PriorityQueue;
-import java.util.Stack;
+import java.util.ArrayDeque;
+import java.util.Deque;
/**
* The ShortestPaths class is designed to compute the shortest paths from a starting vertex
@@ -42,12 +44,23 @@ public boolean hasPathTo(V v) {
* returns an empty iterable if no path exists
*/
public Iterable> pathTo(V target) {
- Stack> edges = new Stack<>();
+ // NOTE an ArrayDeque, not a java.util.Stack. The walk below goes BACKWARDS
+ // from the target, so pushing is what puts the path back into reading
+ // order -- but java.util.Stack extends Vector and iterates bottom-to-top,
+ // in insertion order, which undoes exactly the reversal that pushing is
+ // for. ArrayDeque.push adds at the front and iterates front-to-back, so it
+ // reverses as intended.
+ Deque> edges = new ArrayDeque<>();
if (hasPathTo(target)) {
V v = target;
- for (Vertex vertex = table.get(v); vertex.edgeTo != null; ) {
+ // NOTE the update clause matters: v steps back along the path, so
+ // vertex must be re-read each time round or the check below compares
+ // the edge into the TARGET against a v that has already moved.
+ // That check uses Objects.equals rather than !=, since two equal
+ // vertices need not be the same object.
+ for (Vertex vertex = table.get(v); vertex.edgeTo != null; vertex = table.get(v)) {
Edge edgeTo = vertex.edgeTo;
- if (edgeTo.getTo() != v) throw new RuntimeException("assertion error");
+ if (!Objects.equals(edgeTo.getTo(), v)) throw new RuntimeException("assertion error");
edges.push(edgeTo);
v = edgeTo.getFrom();
}
@@ -179,4 +192,4 @@ public String toString() {
private double cost;
private Edge edgeTo;
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/BFS.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/BFS.java
index 018c4f8f..12d9c7a1 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/BFS.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/BFS.java
@@ -78,4 +78,4 @@ public BFS(int v) {
private final int V; // No. of vertices
private final LinkedList[] adj; //Adjacency Lists
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/Bag.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/Bag.java
index 67108f93..cc2a85ac 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/Bag.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/Bag.java
@@ -139,3 +139,4 @@ public Item next() {
}
/*Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
+
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/Edge.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/Edge.java
index bff76fbf..61aa1dbb 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/Edge.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/Edge.java
@@ -103,4 +103,4 @@ public Edge(int v, int w, double weight) {
private final int v;
private final int w;
private final double weight;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/EdgeWeightedGraph.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/EdgeWeightedGraph.java
index dee042be..b5ef6fa8 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/EdgeWeightedGraph.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/EdgeWeightedGraph.java
@@ -211,4 +211,4 @@ public String toString() {
}
-/*Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
\ No newline at end of file
+/*Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/MinPQ.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/MinPQ.java
index 0204496d..def50673 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/MinPQ.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/MinPQ.java
@@ -267,4 +267,4 @@ public Key next() {
}
-/*Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
\ No newline at end of file
+/*Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/Prims.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/Prims.java
index 8e4963fb..0b56bf19 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/Prims.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/Prims.java
@@ -70,4 +70,4 @@ private void visit(EdgeWeightedGraph G, int v) {
private final boolean[] marked; // MST vertices
private final Queue mst; // MST edges
private final MinPQ pq; // crossing (and ineligible) edges
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/StdRandom.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/StdRandom.java
index 7b255d13..b414de5e 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/StdRandom.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/traversal/StdRandom.java
@@ -575,4 +575,4 @@ private static void validateSubarrayIndices(int from, int to, int length) {
/*
-Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
\ No newline at end of file
+Copyright © 2000–2019, Robert Sedgewick and Kevin Wayne.*/
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/Building.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/Building.java
index 3eedc4f2..d4830e6d 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/Building.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/Building.java
@@ -96,4 +96,4 @@ public Building(int map, String code, String zone, double lon, double lat, boole
private final String code;
private final String name;
private final Position position;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/BuildingLoader.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/BuildingLoader.java
index 3d9ebbee..747fe7f0 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/BuildingLoader.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/BuildingLoader.java
@@ -97,4 +97,4 @@ public static List createBuildings() {
return buildings;
}
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/TunnelProperties.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/TunnelProperties.java
index 8a4a16c9..7c357e29 100644
--- a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/TunnelProperties.java
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/TunnelProperties.java
@@ -93,4 +93,4 @@ public TunnelProperties(long cost, int length, int phase, int sequence) {
final int length;
final int phase;
int sequence;
-}
\ No newline at end of file
+}
diff --git a/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/Tunnels_Gryphon.java b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/Tunnels_Gryphon.java
new file mode 100644
index 00000000..bc0115a8
--- /dev/null
+++ b/Java/src/main/java/com/phasmidsoftware/dsaipg/graphs/tunnels/Tunnels_Gryphon.java
@@ -0,0 +1,243 @@
+package com.phasmidsoftware.dsaipg.graphs.tunnels;
+
+import com.phasmidsoftware.dsaipg.graphs.gis.*;
+import com.phasmidsoftware.dsaipg.graphs.undirected.Edge;
+import com.phasmidsoftware.dsaipg.graphs.undirected.EdgeGraph;
+import com.phasmidsoftware.dsaipg.graphs.undirected.Graph_Edges;
+import com.phasmidsoftware.dsaipg.util.iteration.SizedIterable;
+import com.phasmidsoftware.gryphon.java.MinimumSpanningTree;
+import com.phasmidsoftware.gryphon.java.WeightedEdge;
+import com.phasmidsoftware.gryphon.java.WeightedGraph;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.function.Predicate;
+
+public class Tunnels_Gryphon implements Iterable> {
+
+ public static void main(String[] args) throws IOException {
+ Tunnels_Gryphon ts = new Tunnels_Gryphon(BuildingLoader.createBuildings());
+ Collection> mst = ts.getMst();
+
+ double totalCost = mst.stream()
+ .map(WeightedEdge::attribute)
+ .mapToDouble(tp -> tp.cost)
+ .sum();
+ System.out.println("Total cost: " + totalCost);
+
+ System.out.println("Number of edges: " + mst.size());
+
+ Graph_Edges graphEdges = new Graph_Edges<>();
+ for (WeightedEdge e : mst) {
+ Edge edge = new Edge<>(e.from(), e.to(), e.attribute());
+ graphEdges.addEdge(edge);
+ }
+ Kml kml = new Kml<>(graphEdges);
+ String filename = "tunnels.kml";
+ kml.createKML(new File(filename));
+ System.out.println("Tunnels output to KML file: " + filename);
+// System.out.println("Total cost: " + totalCost + ", total length: " + totalLength);
+ }
+
+ public Tunnels_Gryphon(List buildings) {
+ WeightedGraph g = WeightedGraph.undirectedWeighted();
+ setupZones();
+ setupTunnels();
+ EdgeGraph tunnelGraph = createGraph(buildings, e -> e.getAttribute().length <= 250);
+ SizedIterable> edges = tunnelGraph.edges();
+ for (Edge e : edges) {
+ Building destinationBuilding = e.getOther(e.get());
+ g.addEdge(new WeightedEdge<>(e.get(), destinationBuilding, e.getAttribute()));
+ }
+ mst = MinimumSpanningTree.prim(g, buildings.get(5), Comparator.comparingLong(tp -> tp.cost));
+ }
+
+ /**
+ * Returns an iterator over elements of type {@code T}.
+ *
+ * @return an Iterator.
+ */
+ @NotNull
+ @Override
+ public Iterator> iterator() {
+ return mst.values().iterator();
+ }
+
+ public Collection> getMst() {
+ return mst.values();
+ }
+
+ public Map> getMstMap() {
+ return mst;
+ }
+
+ /**
+ * Create a graph consisting of all possible edges connection the buildings.
+ * The resulting graph should contain N(N-1)/2 edges where N is length of the list buildings.
+ *
+ * @param buildings a list of Buildings.
+ * @param predicate the predicate: only edges satisfying this predicate will be added to the graph.
+ * @return a Geo<Building, TunnelProperties>
+ */
+ private static EdgeGraph createGraph(List buildings, Predicate> predicate) {
+ GeoGraphSpherical