java-topology/defects/numpy/unit/NumpyTest.java

110 lines
4.3 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.*;
/**
* Standalone unit tests for NumPy CWE-407 defects.
*
* numpy-0001: f2py crackfortran _get_depend_dict — O(W²) list dedup in dep resolution
* Simulates the "if w not in words: words.append(w)" inner loop.
* slow(): counts ops using list membership (words.contains(w)) — O(W) per insert
* fast(): counts ops using a parallel HashSet for O(1) membership
* Assert: slowOps >= fastOps * 10 for W=500 deps per variable
*/
public class NumpyTest {
// ── numpy-0001 ─────────────────────────────────────────────────────────────
/**
* Simulates _get_depend_dict slow path.
*
* For each variable, we have a list of direct deps (depSources).
* Each dep expands into more deps — all land in 'words' (a list).
* Every insertion does: if w not in words → O(|words|) scan.
*
* @param depSources transitive dependencies to merge into words (simulates the expansion)
* @return op count (each list.contains() call = 1 op)
*/
static long slowDependDict(List<String> initial, List<String> depSources) {
long ops = 0;
List<String> words = new ArrayList<>(initial);
// Simulate: for word in words[:]: for w in deps[word]: if w not in words: words.append(w)
// We flatten: just insert all depSources into words using linear contains()
for (String w : depSources) {
// O(|words|) scan per candidate
for (int i = 0; i < words.size(); i++) {
ops++; // linear scan cost
if (words.get(i).equals(w)) {
break; // already present, skip
}
if (i == words.size() - 1) {
// not found — append (words grows, next iterations cost more)
words.add(w);
break;
}
}
}
return ops;
}
/**
* Simulates _get_depend_dict fast path.
*
* Parallel HashSet for O(1) membership; list kept for ordering.
*
* @return op count (each HashSet.contains() = 1 op)
*/
static long fastDependDict(List<String> initial, List<String> depSources) {
long ops = 0;
List<String> words = new ArrayList<>(initial);
Set<String> wordsSet = new HashSet<>(initial);
for (String w : depSources) {
ops++; // O(1) set contains
if (!wordsSet.contains(w)) {
words.add(w);
wordsSet.add(w);
}
}
return ops;
}
static void testDependDict() {
int numVars = 1; // single variable with many deps (worst case per variable)
int W = 500; // transitive dep count
// Build unique dep names
List<String> initial = new ArrayList<>();
for (int i = 0; i < 10; i++) initial.add("init_dep_" + i);
// depSources includes duplicates (realistic — many vars share deps)
List<String> depSources = new ArrayList<>();
for (int i = 0; i < W; i++) depSources.add("var_" + i);
// Add duplicates to simulate repeated merges
for (int i = 0; i < W / 2; i++) depSources.add("var_" + i);
long slowOps = slowDependDict(initial, depSources);
long fastOps = fastDependDict(initial, depSources);
System.out.printf(
"numpy-0001 W=%-4d slowOps=%-8d fastOps=%-6d ratio=%.1fx%n",
W, slowOps, fastOps, (double) slowOps / fastOps
);
assert slowOps > fastOps * 10 :
"numpy-0001 FAIL: expected slowOps > 10×fastOps, got " + slowOps + " vs " + fastOps;
System.out.println("numpy-0001 PASS");
}
// ── main ──────────────────────────────────────────────────────────────────
public static void main(String[] args) {
int pass = 0, total = 1;
try { testDependDict(); pass++; } catch (AssertionError e) { System.err.println(e.getMessage()); }
System.out.printf("%n%d/%d PASS%n", pass, total);
if (pass != total) System.exit(1);
}
}