package unit; import support.FindNodeAlgorithm; import support.FindNodeAlgorithm.Result; /** * Unit tests for DEFECT 0002a: InferenceGraph.findNode() O(N) ArrayList scan. * * Proves: * 1. Both implementations find the same nodes (correctness). * 2. Defective version makes exactly N*(N+1)/2 comparisons for N queries. * 3. Fixed version makes exactly N comparisons for N queries. * 4. Doubling N quadruples defective work; doubling N doubles fixed work. * * No build tool required. Compile and run: * * cd tests * java -m jdk.compiler/com.sun.tools.javac.Main -cp . \ * support/FindNodeAlgorithm.java unit/FindNodeComplexityTest.java * java -cp . unit.FindNodeComplexityTest */ public class FindNodeComplexityTest { private static int passed = 0; private static int failed = 0; public static void main(String[] args) { System.out.println("=== FindNodeComplexityTest (DEFECT 0002a) ===\n"); System.out.println("-- Correctness: both finders return same nodes --"); testCorrectnessSmall(); testCorrectnessMedium(); testAllFound(); System.out.println("\n-- Complexity: defective comparison counts N*(N+1)/2 --"); testDefectiveExactCounts(); System.out.println("\n-- Complexity: fixed comparison counts N --"); 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() { Result def = FindNodeAlgorithm.queryAllDefective(5); Result fix = FindNodeAlgorithm.queryAllFixed(5); assertEqual("small: defective found count", 5, def.found.size()); assertEqual("small: fixed found count", 5, fix.found.size()); for (int i = 0; i < 5; i++) { assertTrue("small: defective found[" + i + "] not null", def.found.get(i) != null); assertTrue("small: fixed found[" + i + "] not null", fix.found.get(i) != null); assertEqual("small: defective found[" + i + "] label", "N" + i, def.found.get(i).label); assertEqual("small: fixed found[" + i + "] label", "N" + i, fix.found.get(i).label); } } static void testCorrectnessMedium() { Result def = FindNodeAlgorithm.queryAllDefective(20); Result fix = FindNodeAlgorithm.queryAllFixed(20); assertEqual("medium: defective found count", 20, def.found.size()); assertEqual("medium: fixed found count", 20, fix.found.size()); for (int i = 0; i < 20; i++) { assertEqual("medium: found[" + i + "] labels match", def.found.get(i).label, fix.found.get(i).label); } } static void testAllFound() { // No null results — every query hits Result def = FindNodeAlgorithm.queryAllDefective(10); long nullCount = def.found.stream().filter(n -> n == null).count(); assertEqual("all-found: no nulls in defective", 0L, nullCount); Result fix = FindNodeAlgorithm.queryAllFixed(10); long nullCountFix = fix.found.stream().filter(n -> n == null).count(); assertEqual("all-found: no nulls in fixed", 0L, nullCountFix); } // ─── Exact comparison counts ────────────────────────────────────────────── /** * PROVES DEFECT: querying all N nodes in order 0..N-1 forces exactly * N*(N+1)/2 element comparisons with the ArrayList scan. * * Derivation: query N0 finds it at index 0 (1 comparison), N1 found at * index 1 (2 comparisons), ..., N(k) found at index k (k+1 comparisons). * Total: sum(k+1, k=0..N-1) = N*(N+1)/2. */ static void testDefectiveExactCounts() { System.out.println("[defective] queryAll(N) — expected N*(N+1)/2:"); int[] sizes = {5, 10, 20, 50, 100}; for (int n : sizes) { Result r = FindNodeAlgorithm.queryAllDefective(n); long expected = (long) n * (n + 1) / 2; System.out.printf(" N=%-4d actual=%-8d expected=%-8d %s%n", n, r.comparisons, expected, r.comparisons == expected ? "PASS" : "FAIL expected=" + expected); assertEqual("defective N=" + n, expected, r.comparisons); } } /** * PROVES FIX: N queries against a HashMap require exactly N comparisons — one per lookup. */ static void testFixedExactCounts() { System.out.println("[fixed] queryAll(N) — expected N:"); int[] sizes = {5, 10, 20, 50, 100}; for (int n : sizes) { Result r = FindNodeAlgorithm.queryAllFixed(n); long expected = n; System.out.printf(" N=%-4d actual=%-8d expected=%-8d %s%n", n, r.comparisons, expected, r.comparisons == expected ? "PASS" : "FAIL"); assertEqual("fixed N=" + n, expected, r.comparisons); } } // ─── Growth ratio ───────────────────────────────────────────────────────── /** * PROVES QUADRATIC GROWTH of defect: * Doubling N → defective comparisons quadruple (≈4x), fixed double (≈2x). * * Math: * defective(N) = N*(N+1)/2 ≈ N²/2 * defective(2N) = 2N*(2N+1)/2 ≈ 2N² → ratio ≈ 4 * fixed(N) = N, fixed(2N) = 2N → ratio = 2 (exact) */ static void testGrowthRatio() { int[][] pairs = {{10, 20}, {20, 40}, {50, 100}, {100, 200}}; for (int[] pair : pairs) { int n1 = pair[0], n2 = pair[1]; long def1 = FindNodeAlgorithm.queryAllDefective(n1).comparisons; long def2 = FindNodeAlgorithm.queryAllDefective(n2).comparisons; long fix1 = FindNodeAlgorithm.queryAllFixed(n1).comparisons; long fix2 = FindNodeAlgorithm.queryAllFixed(n2).comparisons; double defRatio = (double) def2 / def1; double fixRatio = (double) fix2 / fix1; System.out.printf(" N %d→%d: defective ratio=%.2f (expect ~4.0) fixed ratio=%.2f (expect 2.0)%n", n1, n2, defRatio, fixRatio); assertTrue("defective N=" + n1 + "→" + n2 + " ratio ≥ 3.8", defRatio >= 3.8); assertTrue("defective N=" + n1 + "→" + n2 + " ratio ≤ 4.2", defRatio <= 4.2); assertTrue("fixed N=" + n1 + "→" + n2 + " 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++; } } }