package support; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Reference implementations of ModuleHashesBuilder$TopoSorter.visit() — defective and fixed. * * DEFECT 0003 (java.base/jdk.internal.module): visit() checks * Deque.contains(node) to detect whether the current node is on the DFS * recursion stack (cycle detection). ArrayDeque.contains() = O(N) linear scan. * This code lives in java.base — present in every JDK/JRE. * * Fix: maintain a HashSet onStack alongside the Deque. * HashSet.contains() = O(1). * * Exact comparison counts for a linear chain of V nodes (N0→N1→...→N(V-1)): * defective: V*(V-1)/2 (visit(N_k) scans k-element stack = k comparisons; * sum(k, k=0..V-1) = V*(V-1)/2) * fixed: V (1 per visit — one HashSet.contains() call) * * Derivation of defective count: * visit(N0): stack=[], deque.contains(N0) scans 0 elements → 0 comparisons * visit(N1): stack=[N0], contains(N1) scans 1 element → 1 comparison * visit(N2): stack=[N1,N0], contains(N2) scans 2 elements → 2 comparisons * ... * visit(N(V-1)): stack is V-1 deep → V-1 comparisons * Total: 0+1+2+...+(V-1) = V*(V-1)/2. * * Growth when V doubles: defective ≈4x (quadratic), fixed ≈2x (linear). */ public class TopoSorterAlgorithm { public static class Node { public final String label; public final List deps = new ArrayList<>(); public Node(String label) { this.label = label; } public void addDep(Node dep) { deps.add(dep); } @Override public String toString() { return label; } } public static class Result { public final List sorted; public final long comparisons; Result(List sorted, long comparisons) { this.sorted = sorted; this.comparisons = comparisons; } } // ─── DEFECTIVE: Deque.contains() for on-stack check ───────────────────── // Mirrors ModuleHashesBuilder$TopoSorter.visit() — uses Deque.contains(m) // to check whether the current node is already on the recursion stack. public static Result topoSortDefective(List nodes) { return new DefectiveSorter().sort(nodes); } private static class DefectiveSorter { private final Deque stack = new ArrayDeque<>(); private final Set visited = new HashSet<>(); private final List sorted = new ArrayList<>(); private long comparisons = 0; Result sort(List nodes) { for (Node n : nodes) { if (!visited.contains(n)) visit(n); } return new Result(sorted, comparisons); } void visit(Node v) { // DEFECT: O(N) scan of the recursion stack for cycle detection comparisons += dequeContains(stack, v); visited.add(v); stack.push(v); for (Node dep : v.deps) { if (!visited.contains(dep)) visit(dep); } stack.pop(); sorted.add(0, v); } private static long dequeContains(Deque deque, Node target) { long count = 0; for (Node n : deque) { count++; if (n == target) break; } return count; } } // ─── FIXED: HashSet for on-stack check ───────────────────────────── // Maintain a dedicated onStack set. HashSet.contains() = O(1). public static Result topoSortFixed(List nodes) { return new FixedSorter().sort(nodes); } private static class FixedSorter { private final Set onStack = new HashSet<>(); private final Set visited = new HashSet<>(); private final List sorted = new ArrayList<>(); private long comparisons = 0; Result sort(List nodes) { for (Node n : nodes) { if (!visited.contains(n)) visit(n); } return new Result(sorted, comparisons); } void visit(Node v) { comparisons++; // O(1) HashSet.contains() for cycle check // onStack.contains(v) would be false here (not a cycle); add anyway visited.add(v); onStack.add(v); for (Node dep : v.deps) { if (!visited.contains(dep)) visit(dep); } onStack.remove(v); sorted.add(0, v); } } // ─── Factory ───────────────────────────────────────────────────────────── /** * Linear chain: N0→N1→...→N(V-1). No cycles — a valid DAG for topo sort. * DFS visits nodes in stack-depth order, producing an increasing stack depth * at each level. * * Defective comparisons: V*(V-1)/2 * Fixed comparisons: V */ public static List buildLinearChain(int v) { List nodes = new ArrayList<>(); for (int i = 0; i < v; i++) nodes.add(new Node("N" + i)); for (int i = 0; i < v - 1; i++) nodes.get(i).addDep(nodes.get(i + 1)); return nodes; } }