package unit; import java.util.*; /** * Standalone unit test for prometheus-0002: CWE-407. * * prometheus-0002: dependencyMap.dependencies() O(R²×D) in AnalyseRules * slow() mirrors the defective pattern: for each of R rules, scan all R * map entries doing a linear list-contains on the dependents slice. * Cost per call: O(R × D); total for AnalyseRules: O(R² × D). * fast() maintains an inverse map built once in O(R×D); each lookup O(1). * Cost per call: O(1); total for AnalyseRules: O(R). * Assert: slowOps > fastOps * 5x for R=200 rules with D=10 dependents each. */ public class Prometheus0002DependenciesTest { /** * Slow path — dependencies(r): iterate entire forward map, slices.Contains per entry. * forwardMap: ruleId → list of ruleIds that depend on it (dependents). */ static long slowAnalyseRules(Map> forwardMap, List rules) { long ops = 0; for (int r : rules) { // dependencies(r): scan all entries for (Map.Entry> e : forwardMap.entrySet()) { // slices.Contains(dependents, r) — O(D) linear scan for (int dep : e.getValue()) { ops++; if (dep == r) break; // early exit on hit, worst case no hit } } } return ops; } /** * Fast path — precompute inverse map; dependencies(r) is O(1) lookup. */ static long fastAnalyseRules(Map> forwardMap, List rules) { long ops = 0; // Build inverse map once: O(R×D) Map> inverseMap = new HashMap<>(); for (Map.Entry> e : forwardMap.entrySet()) { int rule = e.getKey(); for (int dep : e.getValue()) { ops++; // cost to build inverse inverseMap.computeIfAbsent(dep, k -> new ArrayList<>()).add(rule); } } // Now each dependencies(r) lookup is O(1) for (int r : rules) { ops++; // O(1) map lookup List deps = inverseMap.get(r); // use deps (no scan needed) if (deps != null) { ops += deps.size(); // charge for reading results (same in both) } } return ops; } static void testDependencies() { int R = 200; // number of rules int D = 10; // average dependents per rule (fan-out) // Build a synthetic forward dependency map: // rule i has dependents [i+1, i+2, ..., i+D] (mod R) so D deps each List rules = new ArrayList<>(R); for (int i = 0; i < R; i++) rules.add(i); Map> forwardMap = new HashMap<>(); for (int i = 0; i < R; i++) { List dependents = new ArrayList<>(D); for (int j = 1; j <= D; j++) { dependents.add((i + j) % R); } forwardMap.put(i, dependents); } long sOps = slowAnalyseRules(forwardMap, rules); long fOps = fastAnalyseRules(forwardMap, rules); int minRatio = 5; boolean pass = sOps > fOps * minRatio; System.out.printf("prometheus-0002 [R=%d D=%d]: slow=%d fast=%d ratio=%.1fx — %s%n", R, D, sOps, fOps, (double) sOps / fOps, pass ? "PASS" : "FAIL"); if (!pass) throw new AssertionError("prometheus-0002 FAIL: slow=" + sOps + " fast=" + fOps); } public static void main(String[] args) { testDependencies(); System.out.println("1/1 PASS"); } }