Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
180 lines
7.7 KiB
Java
180 lines
7.7 KiB
Java
package unit;
|
|
|
|
import support.ClosureAlgorithm;
|
|
import support.ClosureAlgorithm.Result;
|
|
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* Unit tests for DEFECT 0002b: InferenceGraph.Node.closure() uncached DFS.
|
|
*
|
|
* Proves:
|
|
* 1. Both implementations produce identical closure sets (correctness).
|
|
* 2. Defective version visits exactly K*V nodes for K calls on a V-node chain.
|
|
* 3. Fixed version visits exactly V+(K-1) nodes for the same K calls.
|
|
* 4. Doubling K doubles defective work; fixed is nearly constant.
|
|
*
|
|
* No build tool required. Compile and run:
|
|
*
|
|
* cd tests
|
|
* java -m jdk.compiler/com.sun.tools.javac.Main -cp . \
|
|
* support/ClosureAlgorithm.java unit/ClosureComplexityTest.java
|
|
* java -cp . unit.ClosureComplexityTest
|
|
*/
|
|
public class ClosureComplexityTest {
|
|
|
|
private static int passed = 0;
|
|
private static int failed = 0;
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("=== ClosureComplexityTest (DEFECT 0002b) ===\n");
|
|
|
|
System.out.println("-- Correctness: both versions return same closure set --");
|
|
testCorrectnessSmall();
|
|
testCorrectnessMedium();
|
|
|
|
System.out.println("\n-- Complexity: defective visits K*V nodes --");
|
|
testDefectiveExactCounts();
|
|
|
|
System.out.println("\n-- Complexity: fixed visits V+(K-1) nodes --");
|
|
testFixedExactCounts();
|
|
|
|
System.out.println("\n-- Complexity: growth ratio proves linear vs constant in K --");
|
|
testGrowthRatioInK();
|
|
|
|
System.out.printf("\n%d passed, %d failed%n", passed, failed);
|
|
if (failed > 0) System.exit(1);
|
|
}
|
|
|
|
// ─── Correctness ─────────────────────────────────────────────────────────
|
|
|
|
static void testCorrectnessSmall() {
|
|
int v = 5, k = 3;
|
|
ClosureAlgorithm.Node startDef = ClosureAlgorithm.buildLinearChain(v);
|
|
ClosureAlgorithm.Node startFix = ClosureAlgorithm.buildLinearChain(v);
|
|
|
|
Result def = ClosureAlgorithm.closureDefective(startDef, k);
|
|
Result fix = ClosureAlgorithm.closureFixed(startFix, k);
|
|
|
|
assertEqual("small: defective closure size", v, def.closure.size());
|
|
assertEqual("small: fixed closure size", v, fix.closure.size());
|
|
}
|
|
|
|
static void testCorrectnessMedium() {
|
|
int v = 20, k = 5;
|
|
ClosureAlgorithm.Node startDef = ClosureAlgorithm.buildLinearChain(v);
|
|
ClosureAlgorithm.Node startFix = ClosureAlgorithm.buildLinearChain(v);
|
|
|
|
Result def = ClosureAlgorithm.closureDefective(startDef, k);
|
|
Result fix = ClosureAlgorithm.closureFixed(startFix, k);
|
|
|
|
assertEqual("medium: defective closure size", v, def.closure.size());
|
|
assertEqual("medium: fixed closure size", v, fix.closure.size());
|
|
}
|
|
|
|
// ─── Exact node-visit counts ──────────────────────────────────────────────
|
|
|
|
/**
|
|
* PROVES DEFECT: K calls on a V-node chain visit exactly K*V nodes total.
|
|
* Each call reruns the full DFS with no memory of previous calls.
|
|
*/
|
|
static void testDefectiveExactCounts() {
|
|
System.out.println("[defective] closureDefective(chain(V), K) — expected K*V:");
|
|
int[][] cases = {{10, 5}, {20, 5}, {10, 10}, {20, 10}, {50, 20}};
|
|
for (int[] c : cases) {
|
|
int v = c[0], k = c[1];
|
|
ClosureAlgorithm.Node start = ClosureAlgorithm.buildLinearChain(v);
|
|
Result r = ClosureAlgorithm.closureDefective(start, k);
|
|
long expected = (long) k * v;
|
|
System.out.printf(" V=%-4d K=%-4d actual=%-8d expected=%-8d %s%n",
|
|
v, k, r.nodeVisits, expected,
|
|
r.nodeVisits == expected ? "PASS" : "FAIL expected=" + expected);
|
|
assertEqual("defective V=" + v + " K=" + k, expected, r.nodeVisits);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PROVES FIX: K calls on a V-node chain visit exactly V+(K-1) nodes total.
|
|
* First call runs the DFS (V visits), each of the K-1 subsequent calls
|
|
* finds the cached result (1 visit each).
|
|
*/
|
|
static void testFixedExactCounts() {
|
|
System.out.println("[fixed] closureFixed(chain(V), K) — expected V+(K-1):");
|
|
int[][] cases = {{10, 5}, {20, 5}, {10, 10}, {20, 10}, {50, 20}};
|
|
for (int[] c : cases) {
|
|
int v = c[0], k = c[1];
|
|
ClosureAlgorithm.Node start = ClosureAlgorithm.buildLinearChain(v);
|
|
Result r = ClosureAlgorithm.closureFixed(start, k);
|
|
long expected = v + (k - 1);
|
|
System.out.printf(" V=%-4d K=%-4d actual=%-8d expected=%-8d %s%n",
|
|
v, k, r.nodeVisits, expected,
|
|
r.nodeVisits == expected ? "PASS" : "FAIL expected=" + expected);
|
|
assertEqual("fixed V=" + v + " K=" + k, expected, r.nodeVisits);
|
|
}
|
|
}
|
|
|
|
// ─── Growth ratio (in K dimension) ────────────────────────────────────────
|
|
|
|
/**
|
|
* PROVES LINEAR GROWTH OF DEFECT IN K:
|
|
* Doubling K → defective visits double (≈2x), fixed barely changes.
|
|
*
|
|
* Math (V=100 fixed):
|
|
* defective(K) = K*V → doubles when K doubles
|
|
* defective(2K) = 2K*V → ratio = 2.0 (exact)
|
|
*
|
|
* fixed(K) = V + (K-1)
|
|
* fixed(2K) = V + (2K-1)
|
|
* ratio = (V+2K-1)/(V+K-1) → 1 as V≫K (nearly constant)
|
|
*/
|
|
static void testGrowthRatioInK() {
|
|
int v = 100;
|
|
int[][] kPairs = {{5, 10}, {10, 20}, {20, 40}};
|
|
for (int[] pair : kPairs) {
|
|
int k1 = pair[0], k2 = pair[1];
|
|
|
|
ClosureAlgorithm.Node startDef1 = ClosureAlgorithm.buildLinearChain(v);
|
|
ClosureAlgorithm.Node startDef2 = ClosureAlgorithm.buildLinearChain(v);
|
|
ClosureAlgorithm.Node startFix1 = ClosureAlgorithm.buildLinearChain(v);
|
|
ClosureAlgorithm.Node startFix2 = ClosureAlgorithm.buildLinearChain(v);
|
|
|
|
long def1 = ClosureAlgorithm.closureDefective(startDef1, k1).nodeVisits;
|
|
long def2 = ClosureAlgorithm.closureDefective(startDef2, k2).nodeVisits;
|
|
long fix1 = ClosureAlgorithm.closureFixed(startFix1, k1).nodeVisits;
|
|
long fix2 = ClosureAlgorithm.closureFixed(startFix2, k2).nodeVisits;
|
|
|
|
double defRatio = (double) def2 / def1;
|
|
double fixRatio = (double) fix2 / fix1;
|
|
|
|
System.out.printf(" V=%d K %d→%d: defective ratio=%.2f (expect 2.0) fixed ratio=%.2f (expect <1.2)%n",
|
|
v, k1, k2, defRatio, fixRatio);
|
|
|
|
// Defective doubles exactly when K doubles (defective = K*V)
|
|
assertTrue("defective K=" + k1 + "→" + k2 + " ratio = 2.0", defRatio == 2.0);
|
|
// Fixed changes very little (V dominates K for V=100)
|
|
assertTrue("fixed K=" + k1 + "→" + k2 + " ratio < 1.2", fixRatio < 1.2);
|
|
}
|
|
}
|
|
|
|
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
|
|
static void assertEqual(String name, long expected, long actual) {
|
|
if (expected == actual) {
|
|
System.out.printf(" PASS %s%n", name);
|
|
passed++;
|
|
} else {
|
|
System.out.printf(" FAIL %s expected=%d actual=%d%n", name, expected, actual);
|
|
failed++;
|
|
}
|
|
}
|
|
|
|
static void assertTrue(String name, boolean condition) {
|
|
if (condition) {
|
|
System.out.printf(" PASS %s%n", name);
|
|
passed++;
|
|
} else {
|
|
System.out.printf(" FAIL %s%n", name);
|
|
failed++;
|
|
}
|
|
}
|
|
}
|