128 lines
4.7 KiB
Java
128 lines
4.7 KiB
Java
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<String> walkGraph(List<String[]> classChains) {
|
||
// ext = accumulated known superclass names (as ordered list like R's named list)
|
||
List<String> 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<String> 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<String> walkGraph(List<String[]> classChains) {
|
||
Set<String> whatSet = new HashSet<>(); // the hash env
|
||
List<String> what = new ArrayList<>(); // ordered for reproducibility
|
||
|
||
for (String[] chain : classChains) {
|
||
// exti[!vapply(names(exti), exists, ...)] — O(|chain|)
|
||
List<String> 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<String[]> buildClassChains(int s) {
|
||
// C_i has transitive supers: C_0 through C_{i-1}
|
||
// walkClassGraph processes each intermediate
|
||
List<String[]> 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<String[]> chains = buildClassChains(s);
|
||
|
||
SlowWalkClassGraph slow = new SlowWalkClassGraph();
|
||
FastWalkClassGraph fast = new FastWalkClassGraph();
|
||
|
||
Set<String> slowResult = slow.walkGraph(chains);
|
||
Set<String> 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);
|
||
}
|
||
}
|