package unit; import java.util.*; /** * RSourceWalkClassGraphTest — CWE-407 test for r-source-0002 * * Models .walkClassGraph() dedup logic: * slow: match(names(exti), what) — O(S²) total across loop * fast: hash-env membership — O(S) total * * Test: for S superclasses, slow does ~S*(S+1)/2 comparisons; fast does ~S. * Ratio must be >= 5x at S=50. */ public class RSourceWalkClassGraphTest { // --- SLOW: O(S²) --- // match(names(exti), what): linear scan of what for each name in exti static class SlowWalkClassGraph { long comparisons = 0; // Simulate .walkClassGraph accumulating superclasses // Each iteration adds one new class with a chain of transitive supers Set walkGraph(List classChains) { // ext = accumulated known superclass names (as ordered list like R's named list) List what = new ArrayList<>(); for (String[] chain : classChains) { // exti = chain of superclasses for this intermediate class // Remove already-known: match(names(exti), what) — O(|chain| × |what|) List newOnes = new ArrayList<>(); for (String name : chain) { boolean found = false; for (String known : what) { // O(|what|) linear scan comparisons++; if (known.equals(name)) { found = true; break; } } if (!found) newOnes.add(name); } what.addAll(newOnes); } return new LinkedHashSet<>(what); } } // --- FAST: O(S) --- // Hash-env membership: exists(name, envir=what_set) static class FastWalkClassGraph { long lookups = 0; Set walkGraph(List classChains) { Set whatSet = new HashSet<>(); // the hash env List what = new ArrayList<>(); // ordered for reproducibility for (String[] chain : classChains) { // exti[!vapply(names(exti), exists, ...)] — O(|chain|) List newOnes = new ArrayList<>(); for (String name : chain) { lookups++; if (!whatSet.contains(name)) { newOnes.add(name); } } for (String name : newOnes) { whatSet.add(name); what.add(name); } } return new LinkedHashSet<>(what); } } // Build a diamond-heavy class hierarchy: // S classes C_0..C_S-1, each inheriting from multiple earlier classes static List buildClassChains(int s) { // C_i has transitive supers: C_0 through C_{i-1} // walkClassGraph processes each intermediate List chains = new ArrayList<>(); for (int i = 0; i < s; i++) { // The i-th class contributes i transitive superclasses String[] chain = new String[i]; for (int j = 0; j < i; j++) { chain[j] = "class_" + j; } chains.add(chain); } return chains; } public static void main(String[] args) { int[] sizes = {10, 20, 50, 100}; System.out.println("RSourceWalkClassGraphTest — r-source-0002"); System.out.println(" Pattern: match(names(exti), what) O(S²) vs hash-env O(S)"); System.out.println(); int passed = 0; int total = 0; for (int s : sizes) { List chains = buildClassChains(s); SlowWalkClassGraph slow = new SlowWalkClassGraph(); FastWalkClassGraph fast = new FastWalkClassGraph(); Set slowResult = slow.walkGraph(chains); Set fastResult = fast.walkGraph(chains); boolean sameResult = slowResult.equals(fastResult); double ratio = slow.comparisons > 0 ? (double) slow.comparisons / Math.max(fast.lookups, 1) : 1.0; boolean correctRatio = s >= 20 ? ratio >= 5.0 : ratio >= 2.0; boolean pass = sameResult && correctRatio; total++; if (pass) passed++; System.out.printf(" S=%-4d slow=%7d fast=%5d ratio=%5.1fx same=%b %s%n", s, slow.comparisons, fast.lookups, ratio, sameResult, pass ? "PASS" : "FAIL"); } System.out.println(); System.out.printf("Result: %d/%d PASS%n", passed, total); if (passed < total) System.exit(1); } }