package unit; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * ansible-0001: Role.get_vars() seen-list O(D²) deduplication * * Slow: uses List for seen-set — dep not in seen is O(D) per iteration → O(D²) total * Fast: uses HashSet for seen-set — dep not in seen is O(1) per iteration → O(D) total * * Verifies: slow_ops >= D*(D-1)/2, fast_ops == D, ratio >= 10x at D=200 */ public class AnsibleRoleGetVarsAlgorithm { static long slowOps = 0; static long fastOps = 0; /** Simulated Role dependency — identity-based equality (no custom equals/hashCode) */ static class MockDep { final int id; MockDep(int id) { this.id = id; } } /** * Slow version: seen is an ArrayList — contains() is O(D) * Mirrors: seen = []; if dep not in seen: seen.append(dep) */ static int slowGetVars(List allDependencies) { slowOps = 0; List seen = new ArrayList<>(); int combinedCount = 0; for (MockDep dep : allDependencies) { // Each call to contains() scans the entire seen list — O(seen.size()) for (MockDep s : seen) { slowOps++; if (s == dep) break; // found } // Equivalent of: if dep not in seen boolean inSeen = false; for (MockDep s : seen) { if (s == dep) { inSeen = true; break; } } if (!inSeen) { // combine_vars equivalent combinedCount++; seen.add(dep); } } return combinedCount; } /** * Fast version: seen is a HashSet — contains() is O(1) * Mirrors: seen = set(); if dep not in seen: seen.add(dep) */ static int fastGetVars(List allDependencies) { fastOps = 0; Set seen = new HashSet<>(); int combinedCount = 0; for (MockDep dep : allDependencies) { fastOps++; if (!seen.contains(dep)) { combinedCount++; seen.add(dep); } } return combinedCount; } /** * Build a dependency list with D unique deps, no duplicates (worst case for seen growth). */ static List buildDeps(int D) { List deps = new ArrayList<>(); for (int i = 0; i < D; i++) { deps.add(new MockDep(i)); } return deps; } public static void main(String[] args) { int passed = 0; int total = 0; // Test 1: basic correctness — both produce same combined count { total++; int D = 10; List deps = buildDeps(D); int slowResult = slowGetVars(deps); int fastResult = fastGetVars(deps); boolean ok = (slowResult == D && fastResult == D); System.out.println((ok ? "PASS" : "FAIL") + " [correctness D=" + D + "]: slow=" + slowResult + " fast=" + fastResult); if (ok) passed++; } // Test 2: with duplicates — both should deduplicate identically { total++; int D = 5; List unique = buildDeps(D); List depsWithDups = new ArrayList<>(); for (MockDep dep : unique) { depsWithDups.add(dep); depsWithDups.add(dep); } // each dep appears twice int slowResult = slowGetVars(depsWithDups); int fastResult = fastGetVars(depsWithDups); boolean ok = (slowResult == D && fastResult == D); System.out.println((ok ? "PASS" : "FAIL") + " [dedup D=" + D + " with dupes]: slow=" + slowResult + " fast=" + fastResult); if (ok) passed++; } // Test 3: slow op count is O(D²) — at least D*(D-1)/2 comparisons { total++; int D = 200; List deps = buildDeps(D); slowGetVars(deps); long minExpectedSlowOps = (long) D * (D - 1) / 2; boolean ok = slowOps >= minExpectedSlowOps; System.out.println((ok ? "PASS" : "FAIL") + " [slow O(D²) D=" + D + "]: ops=" + slowOps + " >= " + minExpectedSlowOps); if (ok) passed++; } // Test 4: fast op count is exactly O(D) — one check per dep { total++; int D = 200; List deps = buildDeps(D); fastGetVars(deps); boolean ok = (fastOps == D); System.out.println((ok ? "PASS" : "FAIL") + " [fast O(D) D=" + D + "]: ops=" + fastOps + " == " + D); if (ok) passed++; } // Test 5: ratio >= 10x at D=200 { total++; int D = 200; List deps = buildDeps(D); slowGetVars(deps); long slowCount = slowOps; fastGetVars(deps); long fastCount = fastOps; double ratio = (double) slowCount / fastCount; boolean ok = ratio >= 10.0; System.out.printf((ok ? "PASS" : "FAIL") + " [ratio D=%d]: slowOps=%d fastOps=%d ratio=%.1fx%n", D, slowCount, fastCount, ratio); if (ok) passed++; } System.out.println(passed + "/" + total + " PASS"); if (passed != total) System.exit(1); } }