package unit; import support.TopoSorterAlgorithm; import support.TopoSorterAlgorithm.Result; import java.util.List; /** * Unit tests for DEFECT 0003: ModuleHashesBuilder$TopoSorter Deque.contains(). * * Proves: * 1. Both implementations produce identical topological orderings (correctness). * 2. Defective version makes exactly V*(V-1)/2 comparisons for a V-node chain. * 3. Fixed version makes exactly V comparisons for the same chain. * 4. Doubling V quadruples defective work; doubling V doubles fixed work. * * No build tool required. Compile and run: * * cd tests * java -m jdk.compiler/com.sun.tools.javac.Main -cp . \ * support/TopoSorterAlgorithm.java unit/TopoSorterComplexityTest.java * java -cp . unit.TopoSorterComplexityTest */ public class TopoSorterComplexityTest { private static int passed = 0; private static int failed = 0; public static void main(String[] args) { System.out.println("=== TopoSorterComplexityTest (DEFECT 0003) ===\n"); System.out.println("-- Correctness: both sorters produce the same ordering --"); testCorrectnessSmall(); testCorrectnessMedium(); testCorrectnessSingleNode(); System.out.println("\n-- Complexity: defective comparison counts V*(V-1)/2 --"); testDefectiveExactCounts(); System.out.println("\n-- Complexity: fixed comparison counts V --"); testFixedExactCounts(); System.out.println("\n-- Complexity: growth ratio proves quadratic vs linear --"); testGrowthRatio(); System.out.printf("\n%d passed, %d failed%n", passed, failed); if (failed > 0) System.exit(1); } // ─── Correctness ───────────────────────────────────────────────────────── static void testCorrectnessSmall() { List graph = TopoSorterAlgorithm.buildLinearChain(5); Result def = TopoSorterAlgorithm.topoSortDefective(graph); List graph2 = TopoSorterAlgorithm.buildLinearChain(5); Result fix = TopoSorterAlgorithm.topoSortFixed(graph2); assertEqual("small: defective sorted count", 5, def.sorted.size()); assertEqual("small: fixed sorted count", 5, fix.sorted.size()); // Chain N0→...→N4 sorted as [N0, N1, N2, N3, N4] for (int i = 0; i < 5; i++) { assertEqual("small: defective sorted[" + i + "]", "N" + i, def.sorted.get(i).label); assertEqual("small: fixed sorted[" + i + "]", "N" + i, fix.sorted.get(i).label); } } static void testCorrectnessMedium() { List graph = TopoSorterAlgorithm.buildLinearChain(20); Result def = TopoSorterAlgorithm.topoSortDefective(graph); List graph2 = TopoSorterAlgorithm.buildLinearChain(20); Result fix = TopoSorterAlgorithm.topoSortFixed(graph2); assertEqual("medium: defective sorted count", 20, def.sorted.size()); assertEqual("medium: fixed sorted count", 20, fix.sorted.size()); for (int i = 0; i < 20; i++) { assertEqual("medium: sorted[" + i + "] labels match", def.sorted.get(i).label, fix.sorted.get(i).label); } } static void testCorrectnessSingleNode() { List g1 = TopoSorterAlgorithm.buildLinearChain(1); List g2 = TopoSorterAlgorithm.buildLinearChain(1); Result def = TopoSorterAlgorithm.topoSortDefective(g1); Result fix = TopoSorterAlgorithm.topoSortFixed(g2); assertEqual("single: defective count", 1, def.sorted.size()); assertEqual("single: fixed count", 1, fix.sorted.size()); assertEqual("single: defective comparisons", 0L, def.comparisons); assertEqual("single: fixed comparisons", 1L, fix.comparisons); } // ─── Exact comparison counts ────────────────────────────────────────────── /** * PROVES DEFECT: a V-node chain forces exactly V*(V-1)/2 comparisons with * the Deque.contains() scan. * * Derivation: * visit(N0): stack=[], scan 0 elements → 0 comparisons * visit(N1): stack=[N0], scan 1 element → 1 comparison * visit(N_k): stack is k deep → k comparisons * Total: 0+1+2+...+(V-1) = V*(V-1)/2. */ static void testDefectiveExactCounts() { System.out.println("[defective] linearChain(V) — expected V*(V-1)/2:"); int[] sizes = {5, 10, 20, 50, 100}; for (int v : sizes) { List graph = TopoSorterAlgorithm.buildLinearChain(v); Result r = TopoSorterAlgorithm.topoSortDefective(graph); long expected = (long) v * (v - 1) / 2; System.out.printf(" V=%-4d actual=%-8d expected=%-8d %s%n", v, r.comparisons, expected, r.comparisons == expected ? "PASS" : "FAIL expected=" + expected); assertEqual("defective V=" + v, expected, r.comparisons); } } /** * PROVES FIX: a V-node chain requires exactly V comparisons — one per node visit. */ static void testFixedExactCounts() { System.out.println("[fixed] linearChain(V) — expected V:"); int[] sizes = {5, 10, 20, 50, 100}; for (int v : sizes) { List graph = TopoSorterAlgorithm.buildLinearChain(v); Result r = TopoSorterAlgorithm.topoSortFixed(graph); long expected = v; System.out.printf(" V=%-4d actual=%-8d expected=%-8d %s%n", v, r.comparisons, expected, r.comparisons == expected ? "PASS" : "FAIL"); assertEqual("fixed V=" + v, expected, r.comparisons); } } // ─── Growth ratio ───────────────────────────────────────────────────────── /** * PROVES QUADRATIC GROWTH: * Doubling V → defective comparisons quadruple (≈4x), fixed double (≈2x). * * Math: * defective(V) = V*(V-1)/2 ≈ V²/2 * defective(2V) = 2V*(2V-1)/2 ≈ 2V² → ratio ≈ 4 * fixed(V) = V, fixed(2V) = 2V → ratio = 2 (exact) * * Exact defective ratio = 2V*(2V-1) / (V*(V-1)) = 2*(2V-1)/(V-1) * → approaches 4 as V→∞; ≥3.8 for V≥10. */ static void testGrowthRatio() { int[][] pairs = {{10, 20}, {20, 40}, {50, 100}, {100, 200}}; for (int[] pair : pairs) { int v1 = pair[0], v2 = pair[1]; List g1 = TopoSorterAlgorithm.buildLinearChain(v1); List g2 = TopoSorterAlgorithm.buildLinearChain(v2); List g3 = TopoSorterAlgorithm.buildLinearChain(v1); List g4 = TopoSorterAlgorithm.buildLinearChain(v2); long def1 = TopoSorterAlgorithm.topoSortDefective(g1).comparisons; long def2 = TopoSorterAlgorithm.topoSortDefective(g2).comparisons; long fix1 = TopoSorterAlgorithm.topoSortFixed(g3).comparisons; long fix2 = TopoSorterAlgorithm.topoSortFixed(g4).comparisons; double defRatio = (double) def2 / def1; double fixRatio = (double) fix2 / fix1; System.out.printf(" V %d→%d: defective ratio=%.2f (expect ~4.0) fixed ratio=%.2f (expect 2.0)%n", v1, v2, defRatio, fixRatio); assertTrue("defective V=" + v1 + "→" + v2 + " ratio ≥ 3.8", defRatio >= 3.8); // exact ratio = 2*(2V-1)/(V-1) → 4.22 at V=10, converges to 4.0 assertTrue("defective V=" + v1 + "→" + v2 + " ratio ≤ 4.25", defRatio <= 4.25); assertTrue("fixed V=" + v1 + "→" + v2 + " ratio = 2.0", fixRatio == 2.0); } } // ─── Helpers ───────────────────────────────────────────────────────────── static void assertEqual(String name, long expected, long actual) { if (expected == actual) { System.out.printf(" PASS %s%n", name); passed++; } else { System.out.printf(" FAIL %s expected=%d actual=%d%n", name, expected, actual); failed++; } } static void assertEqual(String name, String expected, String actual) { if (expected.equals(actual)) { System.out.printf(" PASS %s%n", name); passed++; } else { System.out.printf(" FAIL %s expected=%s actual=%s%n", name, expected, actual); failed++; } } static void assertTrue(String name, boolean condition) { if (condition) { System.out.printf(" PASS %s%n", name); passed++; } else { System.out.printf(" FAIL %s%n", name); failed++; } } }