wave16: dart-0001/2/3 + octave-0002 + php-0003/4 + cassandra-0005 + erlang-0003 + chef-0001 — 542/240

This commit is contained in:
russell@unturf.com 2026-03-27 19:36:35 -04:00
parent 31850d5ef6
commit d816d3c74c
18 changed files with 1902 additions and 6 deletions

View file

@ -0,0 +1,178 @@
package unit;
import java.util.*;
/**
* Erlang/OTP CWE-407 unit tests standalone, no JUnit.
*
* erlang-0001 digraph.erl one_path() lists:member on growing visited-list
* lib/stdlib/src/digraph.erl (patch: erlang-0001-one-path-sets.patch)
*
* erlang-0003 code_server.erl merge_path1() lists:member(P, Acc) in recursive loop
* lib/kernel/src/code_server.erl lines 600-609
* Acc grows as each unique path is prepended; lists:member is O(|Acc|)
* per call O(N²) total for N unique paths.
* Fix: track seen paths in a HashSet alongside the accumulator list.
*/
public class ErlangAlgorithm {
// -----------------------------------------------------------------------
// erlang-0001: digraph one_path lists:member on visited list
//
// In DFS graph path-finding, each node visited checks membership against
// the accumulated visited list (Xs in the Erlang code).
// Slow: ArrayList.contains() O(depth) per node
// Fast: HashSet.contains() O(1) per node
// -----------------------------------------------------------------------
/**
* Simulate one_path DFS traversal with a linear visited-list (Erlang original).
* Graph: a long linear chain 012...N-1
* Returns total comparison operations performed.
*/
static long onePathSlowOps(int n) {
// visited list, mirrors Erlang's Xs accumulator (a plain list)
List<Integer> visited = new ArrayList<>(n);
long ops = 0;
// Simulate DFS: at each step, check if next node is already visited
for (int node = 0; node < n; node++) {
// lists:member(node, visited) linear scan
boolean found = false;
for (int i = 0; i < visited.size(); i++) {
ops++;
if (visited.get(i).equals(node)) {
found = true;
break;
}
}
if (!found) {
visited.add(node);
}
}
return ops;
}
/**
* Simulate one_path DFS traversal with a HashSet visited-set (patched).
* Returns total comparison operations performed.
*/
static long onePathFastOps(int n) {
Set<Integer> visited = new HashSet<>(n);
long ops = 0;
for (int node = 0; node < n; node++) {
ops++; // O(1) hash lookup count as single op
if (!visited.contains(node)) {
visited.add(node);
}
}
return ops;
}
// -----------------------------------------------------------------------
// erlang-0003: code_server merge_path1 lists:member(P, Acc) per unique path
//
// merge_path1/3 recursively processes a list of N directory paths.
// For each new path P, it checks whether P is already in Acc (the output
// accumulator) using lists:member/2, which is O(|Acc|).
// Since Acc grows by 1 for each unique P, total cost = 1+2+...+N = O(N²).
//
// This runs at VM startup (add_loader_path) and on every code:add_paths/1 call.
// Large Elixir/OTP deployments routinely have N=500-2000 library directories.
//
// File: lib/kernel/src/code_server.erl lines 600-609
//
// Slow: ArrayList.contains() O(|Acc|) per path O(N²) total
// Fast: HashSet.contains() O(1) per path O(N) total
// -----------------------------------------------------------------------
/**
* Simulate merge_path1 with the original List accumulator.
* Input: N unique directory paths.
* Returns total comparison operations performed.
*/
static long mergePathSlowOps(int n) {
// Acc: the accumulator list (paths seen so far, prepended)
List<String> acc = new ArrayList<>(n);
long ops = 0;
// Simulate the IPath list (empty all paths are new)
// merge_path1([P|Path], IPath, Acc) member check on Acc
for (int i = 0; i < n; i++) {
String path = "/usr/lib/erlang/lib/module-" + i + "/ebin";
// lists:member(P, Acc) O(|Acc|) linear scan
boolean found = false;
for (int j = 0; j < acc.size(); j++) {
ops++;
if (acc.get(j).equals(path)) {
found = true;
break;
}
}
if (!found) {
// IPath1 = exclude(P, IPath) for empty IPath this is a no-op
acc.add(path); // [P|Acc] prepend (modelled as add at end)
}
}
return ops;
}
/**
* Simulate merge_path1 with the patched HashSet seen-set alongside the accumulator.
* Returns total comparison operations performed.
*/
static long mergePathFastOps(int n) {
List<String> acc = new ArrayList<>(n);
Set<String> seen = new HashSet<>(n);
long ops = 0;
for (int i = 0; i < n; i++) {
String path = "/usr/lib/erlang/lib/module-" + i + "/ebin";
ops++; // sets:is_element O(1)
if (!seen.contains(path)) {
seen.add(path);
acc.add(path);
}
}
return ops;
}
// -----------------------------------------------------------------------
// Test runner
// -----------------------------------------------------------------------
public static void main(String[] args) {
int passed = 0;
int failed = 0;
// erlang-0001: digraph one_path
int[] graphSizes = {100, 300, 500, 1000};
for (int n : graphSizes) {
long slow = onePathSlowOps(n);
long fast = onePathFastOps(n);
boolean pass = slow >= fast * 5;
System.out.printf(
"erlang-0001 N=%-5d slow=%8d fast=%8d ratio=%.1fx %s%n",
n, slow, fast, (double) slow / fast, pass ? "PASS" : "FAIL");
if (pass) passed++; else failed++;
}
// erlang-0003: code_server merge_path1
// N represents number of unique library directories (typical: 200-2000)
int[] pathCounts = {200, 500, 1000, 2000};
for (int n : pathCounts) {
long slow = mergePathSlowOps(n);
long fast = mergePathFastOps(n);
// Expected ratio: ~N/2 (triangular sum / N = N/2)
boolean pass = slow >= fast * 5;
System.out.printf(
"erlang-0003 N=%-5d slow=%8d fast=%8d ratio=%.1fx %s%n",
n, slow, fast, (double) slow / fast, pass ? "PASS" : "FAIL");
if (pass) passed++; else failed++;
}
System.out.printf("%nTotal: %d/%d PASS%n", passed, passed + failed);
if (failed > 0) System.exit(1);
}
}