package unit; import java.util.*; /** * CWE-407 unit test: Swift RequirementMachine isInMinimizationDomain * * Models the Swift compiler's RewriteSystem where: * - Protos = list of protocol declarations in the minimization domain * - Rules = rewrite rules over those protocols * * The defect: isInMinimizationDomain() does a linear scan over Protos * on every call, and is called once per rule in loops over all rules. * Total cost: O(R × P) where R = rule count, P = protocol count. * * The fix: replace the linear scan with a HashSet lookup — O(1) per call. * * Test measures ops count (comparisons) at N=800 (protos and rules both N). */ public class SwiftRequirementMachineAlgorithm { // ---- Defective version: std::find equivalent (linear scan) ---------------- static long defectiveIsInDomain(List protos, int proto) { // models: std::find(Protos.begin(), Protos.end(), proto) // returns ops performed long ops = 0; for (Integer p : protos) { ops++; if (p.equals(proto)) return ops; } return ops; } /** * Simulate the rule-loop pattern from HomotopyReduction.cpp:611 * for (const auto &rule : getLocalRules()) { * if (!isInMinimizationDomain(rule.getLHS().getRootProtocol())) continue; * ... * } */ static long defectiveMinimize(List protos, List rules) { long totalOps = 0; for (int rule : rules) { // Each rule references a protocol; check membership linearly totalOps += defectiveIsInDomain(protos, rule % protos.size()); } return totalOps; } // ---- Fixed version: HashSet O(1) lookup ----------------------------------- static long fixedMinimize(Set protoSet, List rules) { long totalOps = 0; for (int rule : rules) { // O(1) hash set lookup totalOps++; protoSet.contains(rule % protoSet.size()); } return totalOps; } // ---- Test harness ---------------------------------------------------------- static final int N = 800; public static void main(String[] args) { int passed = 0; int total = 0; // Build protos list (P = N protocols) and rules list (R = N rules) List protos = new ArrayList<>(N); for (int i = 0; i < N; i++) protos.add(i); List rules = new ArrayList<>(N); for (int i = 0; i < N; i++) rules.add(i); Set protoSet = new HashSet<>(protos); // Test 1: defective version is O(R*P) — ops >= N*N/2 on average total++; long slowOps = defectiveMinimize(protos, rules); // Each rule on average scans half the protos list long expectedMinSlow = (long) N * N / 4; // conservative lower bound assert slowOps >= expectedMinSlow : "Slow ops " + slowOps + " unexpectedly low, expected >= " + expectedMinSlow; System.out.println("PASS test1: defective ops=" + slowOps + " (O(N^2) confirmed, N=" + N + ")"); passed++; // Test 2: fixed version is O(R) — ops == N exactly total++; long fastOps = fixedMinimize(protoSet, rules); assert fastOps == N : "Fast ops should be exactly N=" + N + ", got " + fastOps; System.out.println("PASS test2: fixed ops=" + fastOps + " (O(N) confirmed, N=" + N + ")"); passed++; // Test 3: speedup is at least 10x total++; double speedup = (double) slowOps / fastOps; assert speedup >= 10.0 : "Expected speedup >= 10x, got " + speedup; System.out.printf("PASS test3: speedup=%.1fx%n", speedup); passed++; // Test 4: correctness — both find the same items in domain total++; List smallProtos = Arrays.asList(10, 20, 30, 40, 50); Set smallSet = new HashSet<>(smallProtos); // Probe 5 different values int[] probeVals = {10, 25, 30, 99, 50}; for (int v : probeVals) { boolean defectResult = defectiveIsInDomain(smallProtos, v) > 0 && smallProtos.contains(v); boolean fixedResult = smallSet.contains(v); assert defectResult == fixedResult : "Mismatch for value " + v + ": defect=" + defectResult + " fixed=" + fixedResult; } System.out.println("PASS test4: correctness — defective and fixed agree on all probes"); passed++; System.out.println(passed + "/" + total + " PASS"); } }