package unit; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * CWE-407 unit test: asterisk-0003 * * Models cdr_object_create_public_records() variable merge. * SLOW: nested list traversal with strcasecmp — O(B × V) * FAST: hash set membership check — O(B + V) */ public class CdrVarMergeAlgorithm { static long slowOps = 0; static long fastOps = 0; /** Simulated variable: name -> value pair */ static class Var { String name; String value; Var(String name, String value) { this.name = name; this.value = value; } } /** * SLOW path: nested linear scan to deduplicate variables. * Models the defective AST_LIST_TRAVERSE inside AST_LIST_TRAVERSE. * * @param partyAVars already in varshead * @param partyBVars party_b variables to merge in * @return merged list */ static List mergeVarsSlow(List partyAVars, List partyBVars) { List varshead = new ArrayList<>(partyAVars); for (Var bVar : partyBVars) { // outer: B iterations boolean found = false; for (Var existing : varshead) { // inner: V iterations — O(B×V) slowOps++; if (bVar.name.equalsIgnoreCase(existing.name)) { found = true; break; } } if (!found) { varshead.add(new Var(bVar.name, bVar.value)); } } return varshead; } /** * FAST path: hash set membership check — O(B + V). * Fix: build a HashMap of existing names first, then check in O(1). * * @param partyAVars already in varshead * @param partyBVars party_b variables to merge in * @return merged list */ static List mergeVarsFast(List partyAVars, List partyBVars) { List varshead = new ArrayList<>(partyAVars); HashMap existingNames = new HashMap<>(); for (Var v : partyAVars) { // O(V) build fastOps++; existingNames.put(v.name.toLowerCase(), Boolean.TRUE); } for (Var bVar : partyBVars) { // outer: B iterations fastOps++; // O(1) lookup if (!existingNames.containsKey(bVar.name.toLowerCase())) { varshead.add(new Var(bVar.name, bVar.value)); existingNames.put(bVar.name.toLowerCase(), Boolean.TRUE); } } return varshead; } static boolean runTest(int numVarsA, int numVarsB, int overlap) { // Build party_a vars: var_a_0 ... var_a_(numVarsA-1) List partyA = new ArrayList<>(); for (int i = 0; i < numVarsA; i++) { partyA.add(new Var("var_a_" + i, "val_a_" + i)); } // Build party_b vars: first 'overlap' vars share names with party_a // rest are unique to party_b List partyB = new ArrayList<>(); for (int i = 0; i < overlap; i++) { partyB.add(new Var("var_a_" + i, "val_b_" + i)); // duplicate } for (int i = 0; i < numVarsB - overlap; i++) { partyB.add(new Var("var_b_" + i, "val_b_" + i)); // unique } long slowBefore = slowOps; long fastBefore = fastOps; List slowResult = mergeVarsSlow(partyA, partyB); List fastResult = mergeVarsFast(partyA, partyB); long slowCount = slowOps - slowBefore; long fastCount = fastOps - fastBefore; // Both should produce same merged count: numVarsA + (numVarsB - overlap) unique vars int expectedSize = numVarsA + (numVarsB - overlap); if (slowResult.size() != expectedSize) { System.out.println("FAIL: slow result size " + slowResult.size() + " expected " + expectedSize); return false; } if (fastResult.size() != expectedSize) { System.out.println("FAIL: fast result size " + fastResult.size() + " expected " + expectedSize); return false; } // Slow ops should be at least numVarsB (inner loop on each), approx numVarsB * numVarsA // Fast ops should be at most numVarsA + numVarsB double ratio = (double) slowCount / (double) fastCount; System.out.printf(" N=%d B=%d overlap=%d | slowOps=%d fastOps=%d ratio=%.1fx%n", numVarsA, numVarsB, overlap, slowCount, fastCount, ratio); if (ratio < 5.0) { System.out.printf("FAIL: ratio %.1fx < 5x threshold%n", ratio); return false; } return true; } public static void main(String[] args) { int pass = 0; int fail = 0; System.out.println("=== asterisk-0003: CDR Variable Merge O(B×V) ==="); System.out.println(); // Test cases: (numVarsA, numVarsB, overlap) int[][] tests = { {20, 20, 5}, {50, 50, 10}, {100, 100, 20}, {200, 200, 50}, {500, 500, 100}, }; for (int[] t : tests) { slowOps = 0; fastOps = 0; boolean ok = runTest(t[0], t[1], t[2]); if (ok) { pass++; } else { fail++; } } System.out.println(); // Verify quadratic growth in slow path System.out.println("Quadratic growth verification (slow path):"); for (int n : new int[]{10, 50, 100, 200}) { slowOps = 0; fastOps = 0; mergeVarsSlow(buildVarList("a", n), buildVarList("b", n)); System.out.printf(" N=%d slow_ops=%d (expected ~%d quadratic)%n", n, slowOps, n * n); } System.out.println(); System.out.printf("%d/%d PASS%n", pass, pass + fail); if (fail > 0) { System.exit(1); } } static List buildVarList(String prefix, int n) { List list = new ArrayList<>(); for (int i = 0; i < n; i++) { list.add(new Var(prefix + "_var_" + i, "val_" + i)); } return list; } }