whitepaper: 366/178 — wave5 defect tables + PDF rebuild

This commit is contained in:
russell@unturf.com 2026-03-27 15:37:42 -04:00
parent 835ae73b0f
commit a4b0cf4edd
79 changed files with 3829 additions and 17 deletions

View file

@ -0,0 +1,149 @@
package unit;
import java.util.*;
/**
* CWE-407 unit test: Crystal compare_strictness named arg O(N²) lookup
*
* Models Crystal's DefWithMetadata#compare_strictness where:
* - Each def has a list of named parameters (args with external names)
* - For each named arg in self, we search for it in other's named args
* - This is O(N²) where N = number of named parameters
*
* The defect is in restrictions.cr:94,104,141,148 called from
* add_def in types.cr:919 inside a loop over existing overloads.
*
* Test measures ops count at N=800 (named args per def).
*/
public class CrystalCompareStrictnessAlgorithm {
// ---- Defective version: Array#any? / Array#find (linear scan) -------------
/**
* For each name in selfArgs, scan otherArgs linearly.
* Returns total comparisons performed.
*/
static long defectiveCompareNamedArgs(List<String> selfArgs, List<String> otherArgs) {
long ops = 0;
for (String selfName : selfArgs) {
// models: other_named_args.try &.any?(&.external_name.== self_arg.external_name)
for (String otherName : otherArgs) {
ops++;
if (selfName.equals(otherName)) break;
}
}
return ops;
}
/**
* Model add_def loop calling compare_strictness for D overloads.
* Total cost: O(D × N²)
*/
static long defectiveAddDef(int numOverloads, List<String> selfArgs, List<String> otherArgs) {
long totalOps = 0;
for (int i = 0; i < numOverloads; i++) {
totalOps += defectiveCompareNamedArgs(selfArgs, otherArgs);
}
return totalOps;
}
// ---- Fixed version: HashMap O(1) lookup -----------------------------------
/**
* Build a map from name -> arg once, then all lookups are O(1).
*/
static long fixedCompareNamedArgs(List<String> selfArgs, Map<String, String> otherArgMap) {
long ops = 0;
for (String selfName : selfArgs) {
ops++; // O(1) map lookup
otherArgMap.containsKey(selfName);
}
return ops;
}
static long fixedAddDef(int numOverloads, List<String> selfArgs, List<String> otherArgs) {
// Build map once per comparison (still O(N) to build, but only done once)
Map<String, String> otherArgMap = new HashMap<>();
for (String name : otherArgs) otherArgMap.put(name, name);
long totalOps = 0;
for (int i = 0; i < numOverloads; i++) {
totalOps += fixedCompareNamedArgs(selfArgs, otherArgMap);
}
return totalOps;
}
// ---- Test harness ----------------------------------------------------------
static final int N = 800; // named args per def
static final int D = 10; // number of overloads
public static void main(String[] args) {
int passed = 0;
int total = 0;
// Build two arg lists of size N with no overlap (worst case: scan all)
List<String> selfArgs = new ArrayList<>(N);
List<String> otherArgs = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
selfArgs.add("self_" + i);
otherArgs.add("other_" + i); // different names always scans full list
}
// Test 1: defective is O(N²) per comparison call
total++;
long slowOps = defectiveCompareNamedArgs(selfArgs, otherArgs);
// Worst case: every self arg scans all N other args (no match found)
assert slowOps == (long) N * N
: "Expected " + ((long) N * N) + " ops, got " + slowOps;
System.out.println("PASS test1: defective single compare ops=" + slowOps
+ " (N^2=" + ((long) N * N) + ", N=" + N + ")");
passed++;
// Test 2: fixed is O(N) per comparison call
total++;
Map<String, String> otherArgMap = new HashMap<>();
for (String name : otherArgs) otherArgMap.put(name, name);
long fastOps = fixedCompareNamedArgs(selfArgs, otherArgMap);
assert fastOps == N
: "Expected exactly N=" + N + " ops, got " + fastOps;
System.out.println("PASS test2: fixed single compare ops=" + fastOps + " (O(N), N=" + N + ")");
passed++;
// Test 3: speedup per call is >= 10x
total++;
double speedup = (double) slowOps / fastOps;
assert speedup >= 10.0
: "Expected speedup >= 10x, got " + speedup;
System.out.printf("PASS test3: single-compare speedup=%.1fx%n", speedup);
passed++;
// Test 4: with D overloads, defective is O(D*N²), fixed is O(D*N)
total++;
long slowTotal = defectiveAddDef(D, selfArgs, otherArgs);
long fastTotal = fixedAddDef(D, selfArgs, otherArgs);
double addDefSpeedup = (double) slowTotal / fastTotal;
assert addDefSpeedup >= 10.0
: "add_def speedup expected >= 10x, got " + addDefSpeedup;
System.out.printf("PASS test4: add_def D=%d speedup=%.1fx (slow=%d fast=%d)%n",
D, addDefSpeedup, slowTotal, fastTotal);
passed++;
// Test 5: correctness matching names found correctly
total++;
List<String> mixed1 = Arrays.asList("alpha", "beta", "gamma");
List<String> mixed2 = Arrays.asList("delta", "beta", "epsilon");
Map<String, String> mixed2Map = new HashMap<>();
for (String name : mixed2) mixed2Map.put(name, name);
// "beta" should be found in both, "alpha"/"gamma" should not match
assert mixed2.contains("beta");
assert mixed2Map.containsKey("beta");
assert !mixed2.contains("alpha");
assert !mixed2Map.containsKey("alpha");
System.out.println("PASS test5: correctness — defective and fixed agree on membership");
passed++;
System.out.println(passed + "/" + total + " PASS");
}
}