import java.util.*; /** * Unit test for prometheus-0001: dependencyMap.dependencies() O(R²) defect. * * The defect: AnalyseRules() calls dependencies(r) for each of R rules. * dependencies() iterates all entries in the dependency map (R entries) and for * each calls slices.Contains(dependents, r) — an O(D) linear scan of the * dependents slice. Total cost: O(R × R × D) ≈ O(R²) for sparse graphs. * * The fix: build an inverse map (rule → rules it depends on) once in O(R×D), * then each dependencies() call is a single O(1) map lookup. * * This Java simulation models the two strategies, counts operations, and * confirms the asymptotic ratio grows with the number of rules R. */ public class PrometheusTest { // Simulate a Rule as a simple integer ID. static class Rule { final int id; Rule(int id) { this.id = id; } @Override public boolean equals(Object o) { return o instanceof Rule && ((Rule)o).id == id; } @Override public int hashCode() { return Integer.hashCode(id); } @Override public String toString() { return "Rule(" + id + ")"; } } // dependencyMap: rule -> list of rules that depend on it (its "dependents") static Map> buildForwardMap(List rules, int branchFactor) { Map> fwd = new IdentityHashMap<>(); for (int i = 0; i < rules.size(); i++) { Rule rule = rules.get(i); List dependents = new ArrayList<>(); // Simulate: each rule is a dependent of the previous `branchFactor` rules for (int b = 1; b <= branchFactor && i + b < rules.size(); b++) { dependents.add(rules.get(i + b)); } if (!dependents.isEmpty()) { fwd.put(rule, dependents); } } return fwd; } /** * DEFECTIVE dependencies(): scan all forward-map entries for r in dependents. * Returns total operation count (each slices.Contains step = 1 op). */ static long defectiveDependencies(Rule r, Map> fwd) { long ops = 0; List result = new ArrayList<>(); for (Map.Entry> e : fwd.entrySet()) { List dependents = e.getValue(); for (Rule dep : dependents) { // simulates slices.Contains ops++; if (dep.equals(r)) { result.add(e.getKey()); break; } } } return ops; } /** * FIXED dependencies(): O(1) map lookup into pre-built inverse map. */ static long fixedDependencies(Rule r, Map> inv) { long ops = 1; // single map lookup inv.get(r); // O(1) return ops; } /** Build inverse map: O(R*D). */ static Map> buildInverseMap(Map> fwd) { Map> inv = new IdentityHashMap<>(); for (Map.Entry> e : fwd.entrySet()) { for (Rule dep : e.getValue()) { inv.computeIfAbsent(dep, k -> new ArrayList<>()).add(e.getKey()); } } return inv; } /** Simulate AnalyseRules over R rules. */ static long runDefective(List rules, Map> fwd) { long totalOps = 0; for (Rule r : rules) { totalOps += defectiveDependencies(r, fwd); } return totalOps; } static long runFixed(List rules, Map> fwd) { long totalOps = 0; // Build inverse map once Map> inv = buildInverseMap(fwd); // O(R*D) build cost counted separately for (Rule r : rules) { totalOps += fixedDependencies(r, inv); } return totalOps; } public static void main(String[] args) { System.out.println("prometheus-0001: dependencyMap.dependencies() O(R²) defect"); System.out.println("==========================================================="); int[] ruleCounts = {10, 50, 100, 200, 500}; int branchFactor = 3; // each rule depends on 3 next rules boolean allPass = true; for (int R : ruleCounts) { List rules = new ArrayList<>(R); for (int i = 0; i < R; i++) rules.add(new Rule(i)); Map> fwd = buildForwardMap(rules, branchFactor); long defOps = runDefective(rules, fwd); long fixOps = runFixed(rules, fwd); double ratio = (double) defOps / Math.max(fixOps, 1); System.out.printf("R=%4d branchFactor=%d defect_ops=%,8d fixed_ops=%,6d ratio=%.1fx%n", R, branchFactor, defOps, fixOps, ratio); if (ratio < 3.0) { System.err.println(" FAIL: expected ratio >= 3.0 at R=" + R); allPass = false; } } // Correctness: fixed produces same dependencies as defective { int R = 20; List rules = new ArrayList<>(R); for (int i = 0; i < R; i++) rules.add(new Rule(i)); Map> fwd = buildForwardMap(rules, 2); Map> inv = buildInverseMap(fwd); boolean correct = true; for (Rule r : rules) { // Collect via defective List defDeps = new ArrayList<>(); for (Map.Entry> e : fwd.entrySet()) { if (e.getValue().contains(r)) defDeps.add(e.getKey()); } // Collect via fixed List fixDeps = inv.getOrDefault(r, Collections.emptyList()); Set defIds = new HashSet<>(); for (Rule d : defDeps) defIds.add(d.id); Set fixIds = new HashSet<>(); for (Rule d : fixDeps) fixIds.add(d.id); if (!defIds.equals(fixIds)) { System.err.println(" FAIL: mismatch for " + r + " defective=" + defIds + " fixed=" + fixIds); correct = false; allPass = false; } } if (correct) { System.out.println("Correctness check: PASS (dependency sets match)"); } } if (allPass) { System.out.println("ALL TESTS PASS"); System.exit(0); } else { System.out.println("SOME TESTS FAILED"); System.exit(1); } } }