a-maze-ing / CSE 373 PT / heap / test / priorityqueues / AbstractHeapMinPQAssert.java
AbstractHeapMinPQAssert.java
Raw
package priorityqueues;

import org.assertj.core.api.AbstractObjectAssert;

import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * An assert class for your array heap that provides an assert method to check the heap invariant.
 */
public abstract class AbstractHeapMinPQAssert<T>
        extends AbstractObjectAssert<AbstractHeapMinPQAssert<T>, ExtrinsicMinPQ<T>> {

    public AbstractHeapMinPQAssert(ExtrinsicMinPQ<T> actual, Class<?> selfType) {
        super(actual, selfType);
    }

    protected abstract int extractStartIndex(ExtrinsicMinPQ<T> actual);
    protected abstract List<PriorityNode<T>> extractHeap(ExtrinsicMinPQ<T> actual);

    public AbstractHeapMinPQAssert<T> isValid() {
        String message = getErrorMessageIfInvalid();
        if (message != null) {
            this.as("invariant check")
                .failWithMessage(message);
        }
        return this;
    }

    protected String getErrorMessageIfInvalid() {
        List<PriorityNode<T>> heap = extractHeap(this.actual);
        int startIndex = extractStartIndex(this.actual);
        return getErrorMessageIfInvalid(heap, startIndex, startIndex + this.actual.size());
    }

    static <T> String getErrorMessageIfInvalid(List<PriorityNode<T>> heap, int startIndex, int endIndex) {
        if (heap.size() < endIndex) {
            return String.format("Heap's internal ArrayList size (%d) is too small for heap's contents? " +
                    "(Expected at least %d.)",
                heap.size(), endIndex);
        }

        Map<Integer, String> brokenIndices = new TreeMap<>();
        for (int i = startIndex; i < endIndex; i++) {
            String message = checkIndex(heap, i, startIndex, endIndex);
            if (message != null) {
                brokenIndices.put(i, message);
            }
        }
        if (brokenIndices.isEmpty()) {
            return null;
        }
        StringBuilder b = new StringBuilder();
        String nl = System.lineSeparator();
        b.append("Heap invariants broken at:").append(nl);
        for (Map.Entry<Integer, String> entry : brokenIndices.entrySet()) {
            b.append("  - index ").append(entry.getKey())
                .append(" (").append(entry.getValue()).append(")").append(nl);
        }
        return b.toString().stripTrailing();
    }

    private static final int NUM_CHILDREN = 2;
    private static <T> String checkIndex(List<PriorityNode<T>> heap, int parentIndex, int startIndex, int endIndex) {
        PriorityNode<T> parentNode = heap.get(parentIndex);
        int firstChildIndex = (parentIndex - startIndex + 1) * NUM_CHILDREN + startIndex - 1;
        int lastChildIndex = Math.min(NUM_CHILDREN + firstChildIndex, endIndex);
        if (parentNode == null ||
            childrenOfIndexAreInvalid(heap, parentNode, firstChildIndex, lastChildIndex)) {

            List<PriorityNode<T>> children = (firstChildIndex < endIndex)
                ? heap.subList(firstChildIndex, lastChildIndex)
                : List.of();
            return "is " + parentNode + "; has children: " + children.toString();
        }

        return null;
    }

    private static <T> boolean childrenOfIndexAreInvalid(
        List<PriorityNode<T>> heap,
        PriorityNode<T> parentNode,
        int firstChildIndex,
        int lastChildIndex) {
        for (int childIndex = firstChildIndex; childIndex < lastChildIndex; childIndex++) {
            PriorityNode<T> childNode = heap.get(childIndex);

            if (childNode == null || parentNode.getPriority() > childNode.getPriority()) {
                return true;
            }
        }
        return false;
    }
}