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.
190 lines
8 KiB
Java
190 lines
8 KiB
Java
package unit;
|
|
|
|
import support.FindNodeAlgorithm;
|
|
import support.FindNodeAlgorithm.Result;
|
|
|
|
/**
|
|
* Unit tests for DEFECT 0002a: InferenceGraph.findNode() O(N) ArrayList scan.
|
|
*
|
|
* Proves:
|
|
* 1. Both implementations find the same nodes (correctness).
|
|
* 2. Defective version makes exactly N*(N+1)/2 comparisons for N queries.
|
|
* 3. Fixed version makes exactly N comparisons for N queries.
|
|
* 4. Doubling N quadruples defective work; doubling N doubles fixed work.
|
|
*
|
|
* No build tool required. Compile and run:
|
|
*
|
|
* cd tests
|
|
* java -m jdk.compiler/com.sun.tools.javac.Main -cp . \
|
|
* support/FindNodeAlgorithm.java unit/FindNodeComplexityTest.java
|
|
* java -cp . unit.FindNodeComplexityTest
|
|
*/
|
|
public class FindNodeComplexityTest {
|
|
|
|
private static int passed = 0;
|
|
private static int failed = 0;
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("=== FindNodeComplexityTest (DEFECT 0002a) ===\n");
|
|
|
|
System.out.println("-- Correctness: both finders return same nodes --");
|
|
testCorrectnessSmall();
|
|
testCorrectnessMedium();
|
|
testAllFound();
|
|
|
|
System.out.println("\n-- Complexity: defective comparison counts N*(N+1)/2 --");
|
|
testDefectiveExactCounts();
|
|
|
|
System.out.println("\n-- Complexity: fixed comparison counts N --");
|
|
testFixedExactCounts();
|
|
|
|
System.out.println("\n-- Complexity: growth ratio proves quadratic vs linear --");
|
|
testGrowthRatio();
|
|
|
|
System.out.printf("\n%d passed, %d failed%n", passed, failed);
|
|
if (failed > 0) System.exit(1);
|
|
}
|
|
|
|
// ─── Correctness ─────────────────────────────────────────────────────────
|
|
|
|
static void testCorrectnessSmall() {
|
|
Result def = FindNodeAlgorithm.queryAllDefective(5);
|
|
Result fix = FindNodeAlgorithm.queryAllFixed(5);
|
|
assertEqual("small: defective found count", 5, def.found.size());
|
|
assertEqual("small: fixed found count", 5, fix.found.size());
|
|
for (int i = 0; i < 5; i++) {
|
|
assertTrue("small: defective found[" + i + "] not null", def.found.get(i) != null);
|
|
assertTrue("small: fixed found[" + i + "] not null", fix.found.get(i) != null);
|
|
assertEqual("small: defective found[" + i + "] label",
|
|
"N" + i, def.found.get(i).label);
|
|
assertEqual("small: fixed found[" + i + "] label",
|
|
"N" + i, fix.found.get(i).label);
|
|
}
|
|
}
|
|
|
|
static void testCorrectnessMedium() {
|
|
Result def = FindNodeAlgorithm.queryAllDefective(20);
|
|
Result fix = FindNodeAlgorithm.queryAllFixed(20);
|
|
assertEqual("medium: defective found count", 20, def.found.size());
|
|
assertEqual("medium: fixed found count", 20, fix.found.size());
|
|
for (int i = 0; i < 20; i++) {
|
|
assertEqual("medium: found[" + i + "] labels match",
|
|
def.found.get(i).label, fix.found.get(i).label);
|
|
}
|
|
}
|
|
|
|
static void testAllFound() {
|
|
// No null results — every query hits
|
|
Result def = FindNodeAlgorithm.queryAllDefective(10);
|
|
long nullCount = def.found.stream().filter(n -> n == null).count();
|
|
assertEqual("all-found: no nulls in defective", 0L, nullCount);
|
|
|
|
Result fix = FindNodeAlgorithm.queryAllFixed(10);
|
|
long nullCountFix = fix.found.stream().filter(n -> n == null).count();
|
|
assertEqual("all-found: no nulls in fixed", 0L, nullCountFix);
|
|
}
|
|
|
|
// ─── Exact comparison counts ──────────────────────────────────────────────
|
|
|
|
/**
|
|
* PROVES DEFECT: querying all N nodes in order 0..N-1 forces exactly
|
|
* N*(N+1)/2 element comparisons with the ArrayList scan.
|
|
*
|
|
* Derivation: query N0 finds it at index 0 (1 comparison), N1 found at
|
|
* index 1 (2 comparisons), ..., N(k) found at index k (k+1 comparisons).
|
|
* Total: sum(k+1, k=0..N-1) = N*(N+1)/2.
|
|
*/
|
|
static void testDefectiveExactCounts() {
|
|
System.out.println("[defective] queryAll(N) — expected N*(N+1)/2:");
|
|
int[] sizes = {5, 10, 20, 50, 100};
|
|
for (int n : sizes) {
|
|
Result r = FindNodeAlgorithm.queryAllDefective(n);
|
|
long expected = (long) n * (n + 1) / 2;
|
|
System.out.printf(" N=%-4d actual=%-8d expected=%-8d %s%n",
|
|
n, r.comparisons, expected,
|
|
r.comparisons == expected ? "PASS" : "FAIL expected=" + expected);
|
|
assertEqual("defective N=" + n, expected, r.comparisons);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PROVES FIX: N queries against a HashMap require exactly N comparisons — one per lookup.
|
|
*/
|
|
static void testFixedExactCounts() {
|
|
System.out.println("[fixed] queryAll(N) — expected N:");
|
|
int[] sizes = {5, 10, 20, 50, 100};
|
|
for (int n : sizes) {
|
|
Result r = FindNodeAlgorithm.queryAllFixed(n);
|
|
long expected = n;
|
|
System.out.printf(" N=%-4d actual=%-8d expected=%-8d %s%n",
|
|
n, r.comparisons, expected,
|
|
r.comparisons == expected ? "PASS" : "FAIL");
|
|
assertEqual("fixed N=" + n, expected, r.comparisons);
|
|
}
|
|
}
|
|
|
|
// ─── Growth ratio ─────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* PROVES QUADRATIC GROWTH of defect:
|
|
* Doubling N → defective comparisons quadruple (≈4x), fixed double (≈2x).
|
|
*
|
|
* Math:
|
|
* defective(N) = N*(N+1)/2 ≈ N²/2
|
|
* defective(2N) = 2N*(2N+1)/2 ≈ 2N² → ratio ≈ 4
|
|
* fixed(N) = N, fixed(2N) = 2N → ratio = 2 (exact)
|
|
*/
|
|
static void testGrowthRatio() {
|
|
int[][] pairs = {{10, 20}, {20, 40}, {50, 100}, {100, 200}};
|
|
for (int[] pair : pairs) {
|
|
int n1 = pair[0], n2 = pair[1];
|
|
|
|
long def1 = FindNodeAlgorithm.queryAllDefective(n1).comparisons;
|
|
long def2 = FindNodeAlgorithm.queryAllDefective(n2).comparisons;
|
|
long fix1 = FindNodeAlgorithm.queryAllFixed(n1).comparisons;
|
|
long fix2 = FindNodeAlgorithm.queryAllFixed(n2).comparisons;
|
|
|
|
double defRatio = (double) def2 / def1;
|
|
double fixRatio = (double) fix2 / fix1;
|
|
|
|
System.out.printf(" N %d→%d: defective ratio=%.2f (expect ~4.0) fixed ratio=%.2f (expect 2.0)%n",
|
|
n1, n2, defRatio, fixRatio);
|
|
|
|
assertTrue("defective N=" + n1 + "→" + n2 + " ratio ≥ 3.8", defRatio >= 3.8);
|
|
assertTrue("defective N=" + n1 + "→" + n2 + " ratio ≤ 4.2", defRatio <= 4.2);
|
|
assertTrue("fixed N=" + n1 + "→" + n2 + " ratio = 2.0", fixRatio == 2.0);
|
|
}
|
|
}
|
|
|
|
// ─── 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 assertEqual(String name, String expected, String actual) {
|
|
if (expected.equals(actual)) {
|
|
System.out.printf(" PASS %s%n", name);
|
|
passed++;
|
|
} else {
|
|
System.out.printf(" FAIL %s expected=%s actual=%s%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++;
|
|
}
|
|
}
|
|
}
|