java-topology/tests/support/BoundSetAlgorithm.java
russell@unturf.com 0a580b313d undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
2026-03-26 17:11:57 -04:00

109 lines
4.5 KiB
Java

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<Integer> b1, List<Integer> 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<Integer> haystack,
List<Integer> 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<Integer> b1, List<Integer> b2) {
Set<Integer> set1 = new LinkedHashSet<>(b1);
Set<Integer> 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<Integer> buildForwardList(int b) {
List<Integer> 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<Integer> buildReversedList(int b) {
List<Integer> list = new ArrayList<>();
for (int i = b - 1; i >= 0; i--) list.add(i);
return list;
}
}