package support; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; /** * Reference implementations of InferenceContext.isEquiv() — defective and fixed. * * DEFECT 0005 (jdk.compiler, InferenceContext.java:506): * isEquiv() uses List.containsAll() for set-equality comparison of bound lists. * Called twice (mutual containment): !b1.containsAll(b2) || !b2.containsAll(b1). * com.sun.tools.javac.util.List is a linked list — contains() is O(N). * * Fix: convert to LinkedHashSet and use Set.equals() — O(B) total. * * Exact comparison counts for equal sets of size B (forward vs reversed order): * defective: B*(B+1) (two containsAll calls, each = B*(B+1)/2) * fixed: B (one Set.equals check = B membership tests) * * Derivation of defective count: * b1 = [0, 1, ..., B-1], b2 = [B-1, B-2, ..., 0] (reversed — worst order). * b1.containsAll(b2): for each element x in b2, scan b1 until x found: * x=B-1: scan to index B-1 → B comparisons * x=B-2: scan to index B-2 → B-1 comparisons * ... * x=0: scan to index 0 → 1 comparison * Total: B + (B-1) + ... + 1 = B*(B+1)/2. * b2.containsAll(b1): symmetric = B*(B+1)/2. * Both calls complete because the sets are equal (|| short-circuits only on false). * Grand total: B*(B+1). * * Growth when B doubles: defective ≈4x (quadratic), fixed ≈2x (linear). */ public class BoundSetAlgorithm { public static class Result { public final boolean equivalent; public final long comparisons; Result(boolean equivalent, long comparisons) { this.equivalent = equivalent; this.comparisons = comparisons; } } // ─── DEFECTIVE: List.containsAll() — O(B²) ─────────────────────────────── // Mirrors InferenceContext.isEquiv():506 — two mutual containsAll() checks. public static Result isEquivDefective(List b1, List b2) { long[] comparisons = {0}; if (!listContainsAll(b1, b2, comparisons)) return new Result(false, comparisons[0]); if (!listContainsAll(b2, b1, comparisons)) return new Result(false, comparisons[0]); return new Result(true, comparisons[0]); } private static boolean listContainsAll(List haystack, List needles, long[] comparisons) { for (Integer needle : needles) { boolean found = false; for (Integer h : haystack) { comparisons[0]++; if (h.equals(needle)) { found = true; break; } } if (!found) return false; } return true; } // ─── FIXED: Set.equals() — O(B) ───────────────────────────────────────── // Convert both lists to LinkedHashSet; use Set.equals() for comparison. // Set.equals() iterates one set and calls contains() on the other: O(B) total. public static Result isEquivFixed(List b1, List b2) { Set set1 = new LinkedHashSet<>(b1); Set set2 = new LinkedHashSet<>(b2); long comparisons = 0; if (set1.size() != set2.size()) return new Result(false, comparisons); for (Integer x : set1) { comparisons++; // one O(1) HashSet.contains() if (!set2.contains(x)) return new Result(false, comparisons); } return new Result(true, comparisons); } // ─── Factories ─────────────────────────────────────────────────────────── /** * Forward list: [0, 1, 2, ..., B-1]. * Use as b1 — b2 reversed produces worst-case containsAll scan order. */ public static List buildForwardList(int b) { List list = new ArrayList<>(); for (int i = 0; i < b; i++) list.add(i); return list; } /** * Reversed list: [B-1, B-2, ..., 0]. * Use as b2 — each element found at the far end of the forward b1 list. * This maximises the scan depth for each containsAll() lookup. */ public static List buildReversedList(int b) { List list = new ArrayList<>(); for (int i = b - 1; i >= 0; i--) list.add(i); return list; } }