package unit; import support.DependencyListAlgorithm; import support.DependencyListAlgorithm.Result; /** * Unit tests for DEFECT 0004: Dependencies$Node.addDependency() List.contains(). * * Proves: * 1. Both implementations produce the same deduplicated list (correctness). * 2. Defective version makes exactly M*(M-1)/2 comparisons for M unique items. * 3. Fixed version makes exactly M comparisons for M unique items. * 4. Doubling M quadruples defective work; doubling M doubles fixed work. * * No build tool required. Compile and run: * * cd tests * java -m jdk.compiler/com.sun.tools.javac.Main -cp . \ * support/DependencyListAlgorithm.java unit/DependencyListComplexityTest.java * java -cp . unit.DependencyListComplexityTest */ public class DependencyListComplexityTest { private static int passed = 0; private static int failed = 0; public static void main(String[] args) { System.out.println("=== DependencyListComplexityTest (DEFECT 0004) ===\n"); System.out.println("-- Correctness: both produce same deduplicated list --"); testCorrectnessUniqueItems(); testCorrectnessDuplicates(); testCorrectnessEmpty(); System.out.println("\n-- Complexity: defective comparison counts M*(M-1)/2 --"); testDefectiveExactCounts(); System.out.println("\n-- Complexity: fixed comparison counts M --"); 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 testCorrectnessUniqueItems() { int[] items = DependencyListAlgorithm.buildUniqueSequence(5); Result def = DependencyListAlgorithm.addUniqueItemsDefective(items); Result fix = DependencyListAlgorithm.addUniqueItemsFixed(items); assertEqual("unique-5: defective size", 5, def.items.size()); assertEqual("unique-5: fixed size", 5, fix.items.size()); for (int i = 0; i < 5; i++) { assertEqual("unique-5: defective[" + i + "]", (long) i, def.items.get(i).longValue()); assertEqual("unique-5: fixed[" + i + "]", (long) i, fix.items.get(i).longValue()); } } static void testCorrectnessDuplicates() { // [0, 1, 0, 2, 1, 3] → unique = [0, 1, 2, 3] int[] items = {0, 1, 0, 2, 1, 3}; Result def = DependencyListAlgorithm.addUniqueItemsDefective(items); Result fix = DependencyListAlgorithm.addUniqueItemsFixed(items); assertEqual("dupes: defective unique count", 4, def.items.size()); assertEqual("dupes: fixed unique count", 4, fix.items.size()); for (int i = 0; i < 4; i++) { assertEqual("dupes: defective[" + i + "]", (long) i, def.items.get(i).longValue()); assertEqual("dupes: fixed[" + i + "]", (long) i, fix.items.get(i).longValue()); } } static void testCorrectnessEmpty() { int[] items = {}; Result def = DependencyListAlgorithm.addUniqueItemsDefective(items); Result fix = DependencyListAlgorithm.addUniqueItemsFixed(items); assertEqual("empty: defective size", 0, def.items.size()); assertEqual("empty: fixed size", 0, fix.items.size()); assertEqual("empty: defective comparisons", 0L, def.comparisons); assertEqual("empty: fixed comparisons", 0L, fix.comparisons); } // ─── Exact comparison counts ────────────────────────────────────────────── /** * PROVES DEFECT: adding M unique items forces exactly M*(M-1)/2 comparisons. * * Derivation: * Add item 0: list=[], scan 0 elements → 0 comparisons. * Add item 1: list=[0], scan [0] for 1 → 1 comparison (not found). * Add item k: list has k elements → k comparisons (not found). * Total: 0+1+2+...+(M-1) = M*(M-1)/2. */ static void testDefectiveExactCounts() { System.out.println("[defective] addUnique(M) — expected M*(M-1)/2:"); int[] sizes = {5, 10, 20, 50, 100}; for (int m : sizes) { int[] items = DependencyListAlgorithm.buildUniqueSequence(m); Result r = DependencyListAlgorithm.addUniqueItemsDefective(items); long expected = (long) m * (m - 1) / 2; System.out.printf(" M=%-4d actual=%-8d expected=%-8d %s%n", m, r.comparisons, expected, r.comparisons == expected ? "PASS" : "FAIL expected=" + expected); assertEqual("defective M=" + m, expected, r.comparisons); } } /** * PROVES FIX: M unique items require exactly M comparisons — one per set.add(). */ static void testFixedExactCounts() { System.out.println("[fixed] addUnique(M) — expected M:"); int[] sizes = {5, 10, 20, 50, 100}; for (int m : sizes) { int[] items = DependencyListAlgorithm.buildUniqueSequence(m); Result r = DependencyListAlgorithm.addUniqueItemsFixed(items); long expected = m; System.out.printf(" M=%-4d actual=%-8d expected=%-8d %s%n", m, r.comparisons, expected, r.comparisons == expected ? "PASS" : "FAIL"); assertEqual("fixed M=" + m, expected, r.comparisons); } } // ─── Growth ratio ───────────────────────────────────────────────────────── /** * PROVES QUADRATIC GROWTH: * Doubling M → defective comparisons quadruple (≈4x), fixed double (≈2x). * * Math: * defective(M) = M*(M-1)/2 ≈ M²/2 * defective(2M) = 2M*(2M-1)/2 ≈ 2M² → ratio ≈ 4 * fixed(M) = M, fixed(2M) = 2M → ratio = 2 (exact) */ static void testGrowthRatio() { int[][] pairs = {{10, 20}, {20, 40}, {50, 100}, {100, 200}}; for (int[] pair : pairs) { int m1 = pair[0], m2 = pair[1]; long def1 = DependencyListAlgorithm.addUniqueItemsDefective( DependencyListAlgorithm.buildUniqueSequence(m1)).comparisons; long def2 = DependencyListAlgorithm.addUniqueItemsDefective( DependencyListAlgorithm.buildUniqueSequence(m2)).comparisons; long fix1 = DependencyListAlgorithm.addUniqueItemsFixed( DependencyListAlgorithm.buildUniqueSequence(m1)).comparisons; long fix2 = DependencyListAlgorithm.addUniqueItemsFixed( DependencyListAlgorithm.buildUniqueSequence(m2)).comparisons; double defRatio = (double) def2 / def1; double fixRatio = (double) fix2 / fix1; System.out.printf(" M %d→%d: defective ratio=%.2f (expect ~4.0) fixed ratio=%.2f (expect 2.0)%n", m1, m2, defRatio, fixRatio); assertTrue("defective M=" + m1 + "→" + m2 + " ratio ≥ 3.8", defRatio >= 3.8); // exact ratio = 2*(2M-1)/(M-1) → 4.22 at M=10, converges to 4.0 assertTrue("defective M=" + m1 + "→" + m2 + " ratio ≤ 4.25", defRatio <= 4.25); assertTrue("fixed M=" + m1 + "→" + m2 + " 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 assertTrue(String name, boolean condition) { if (condition) { System.out.printf(" PASS %s%n", name); passed++; } else { System.out.printf(" FAIL %s%n", name); failed++; } } }