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.
210 lines
9.2 KiB
Java
210 lines
9.2 KiB
Java
package unit;
|
|
|
|
import support.TopoSorterAlgorithm;
|
|
import support.TopoSorterAlgorithm.Result;
|
|
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Unit tests for DEFECT 0003: ModuleHashesBuilder$TopoSorter Deque.contains().
|
|
*
|
|
* Proves:
|
|
* 1. Both implementations produce identical topological orderings (correctness).
|
|
* 2. Defective version makes exactly V*(V-1)/2 comparisons for a V-node chain.
|
|
* 3. Fixed version makes exactly V comparisons for the same chain.
|
|
* 4. Doubling V quadruples defective work; doubling V doubles fixed work.
|
|
*
|
|
* No build tool required. Compile and run:
|
|
*
|
|
* cd tests
|
|
* java -m jdk.compiler/com.sun.tools.javac.Main -cp . \
|
|
* support/TopoSorterAlgorithm.java unit/TopoSorterComplexityTest.java
|
|
* java -cp . unit.TopoSorterComplexityTest
|
|
*/
|
|
public class TopoSorterComplexityTest {
|
|
|
|
private static int passed = 0;
|
|
private static int failed = 0;
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("=== TopoSorterComplexityTest (DEFECT 0003) ===\n");
|
|
|
|
System.out.println("-- Correctness: both sorters produce the same ordering --");
|
|
testCorrectnessSmall();
|
|
testCorrectnessMedium();
|
|
testCorrectnessSingleNode();
|
|
|
|
System.out.println("\n-- Complexity: defective comparison counts V*(V-1)/2 --");
|
|
testDefectiveExactCounts();
|
|
|
|
System.out.println("\n-- Complexity: fixed comparison counts V --");
|
|
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() {
|
|
List<TopoSorterAlgorithm.Node> graph = TopoSorterAlgorithm.buildLinearChain(5);
|
|
Result def = TopoSorterAlgorithm.topoSortDefective(graph);
|
|
|
|
List<TopoSorterAlgorithm.Node> graph2 = TopoSorterAlgorithm.buildLinearChain(5);
|
|
Result fix = TopoSorterAlgorithm.topoSortFixed(graph2);
|
|
|
|
assertEqual("small: defective sorted count", 5, def.sorted.size());
|
|
assertEqual("small: fixed sorted count", 5, fix.sorted.size());
|
|
// Chain N0→...→N4 sorted as [N0, N1, N2, N3, N4]
|
|
for (int i = 0; i < 5; i++) {
|
|
assertEqual("small: defective sorted[" + i + "]", "N" + i, def.sorted.get(i).label);
|
|
assertEqual("small: fixed sorted[" + i + "]", "N" + i, fix.sorted.get(i).label);
|
|
}
|
|
}
|
|
|
|
static void testCorrectnessMedium() {
|
|
List<TopoSorterAlgorithm.Node> graph = TopoSorterAlgorithm.buildLinearChain(20);
|
|
Result def = TopoSorterAlgorithm.topoSortDefective(graph);
|
|
|
|
List<TopoSorterAlgorithm.Node> graph2 = TopoSorterAlgorithm.buildLinearChain(20);
|
|
Result fix = TopoSorterAlgorithm.topoSortFixed(graph2);
|
|
|
|
assertEqual("medium: defective sorted count", 20, def.sorted.size());
|
|
assertEqual("medium: fixed sorted count", 20, fix.sorted.size());
|
|
for (int i = 0; i < 20; i++) {
|
|
assertEqual("medium: sorted[" + i + "] labels match",
|
|
def.sorted.get(i).label, fix.sorted.get(i).label);
|
|
}
|
|
}
|
|
|
|
static void testCorrectnessSingleNode() {
|
|
List<TopoSorterAlgorithm.Node> g1 = TopoSorterAlgorithm.buildLinearChain(1);
|
|
List<TopoSorterAlgorithm.Node> g2 = TopoSorterAlgorithm.buildLinearChain(1);
|
|
Result def = TopoSorterAlgorithm.topoSortDefective(g1);
|
|
Result fix = TopoSorterAlgorithm.topoSortFixed(g2);
|
|
assertEqual("single: defective count", 1, def.sorted.size());
|
|
assertEqual("single: fixed count", 1, fix.sorted.size());
|
|
assertEqual("single: defective comparisons", 0L, def.comparisons);
|
|
assertEqual("single: fixed comparisons", 1L, fix.comparisons);
|
|
}
|
|
|
|
// ─── Exact comparison counts ──────────────────────────────────────────────
|
|
|
|
/**
|
|
* PROVES DEFECT: a V-node chain forces exactly V*(V-1)/2 comparisons with
|
|
* the Deque.contains() scan.
|
|
*
|
|
* Derivation:
|
|
* visit(N0): stack=[], scan 0 elements → 0 comparisons
|
|
* visit(N1): stack=[N0], scan 1 element → 1 comparison
|
|
* visit(N_k): stack is k deep → k comparisons
|
|
* Total: 0+1+2+...+(V-1) = V*(V-1)/2.
|
|
*/
|
|
static void testDefectiveExactCounts() {
|
|
System.out.println("[defective] linearChain(V) — expected V*(V-1)/2:");
|
|
int[] sizes = {5, 10, 20, 50, 100};
|
|
for (int v : sizes) {
|
|
List<TopoSorterAlgorithm.Node> graph = TopoSorterAlgorithm.buildLinearChain(v);
|
|
Result r = TopoSorterAlgorithm.topoSortDefective(graph);
|
|
long expected = (long) v * (v - 1) / 2;
|
|
System.out.printf(" V=%-4d actual=%-8d expected=%-8d %s%n",
|
|
v, r.comparisons, expected,
|
|
r.comparisons == expected ? "PASS" : "FAIL expected=" + expected);
|
|
assertEqual("defective V=" + v, expected, r.comparisons);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PROVES FIX: a V-node chain requires exactly V comparisons — one per node visit.
|
|
*/
|
|
static void testFixedExactCounts() {
|
|
System.out.println("[fixed] linearChain(V) — expected V:");
|
|
int[] sizes = {5, 10, 20, 50, 100};
|
|
for (int v : sizes) {
|
|
List<TopoSorterAlgorithm.Node> graph = TopoSorterAlgorithm.buildLinearChain(v);
|
|
Result r = TopoSorterAlgorithm.topoSortFixed(graph);
|
|
long expected = v;
|
|
System.out.printf(" V=%-4d actual=%-8d expected=%-8d %s%n",
|
|
v, r.comparisons, expected,
|
|
r.comparisons == expected ? "PASS" : "FAIL");
|
|
assertEqual("fixed V=" + v, expected, r.comparisons);
|
|
}
|
|
}
|
|
|
|
// ─── Growth ratio ─────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* PROVES QUADRATIC GROWTH:
|
|
* Doubling V → defective comparisons quadruple (≈4x), fixed double (≈2x).
|
|
*
|
|
* Math:
|
|
* defective(V) = V*(V-1)/2 ≈ V²/2
|
|
* defective(2V) = 2V*(2V-1)/2 ≈ 2V² → ratio ≈ 4
|
|
* fixed(V) = V, fixed(2V) = 2V → ratio = 2 (exact)
|
|
*
|
|
* Exact defective ratio = 2V*(2V-1) / (V*(V-1)) = 2*(2V-1)/(V-1)
|
|
* → approaches 4 as V→∞; ≥3.8 for V≥10.
|
|
*/
|
|
static void testGrowthRatio() {
|
|
int[][] pairs = {{10, 20}, {20, 40}, {50, 100}, {100, 200}};
|
|
for (int[] pair : pairs) {
|
|
int v1 = pair[0], v2 = pair[1];
|
|
|
|
List<TopoSorterAlgorithm.Node> g1 = TopoSorterAlgorithm.buildLinearChain(v1);
|
|
List<TopoSorterAlgorithm.Node> g2 = TopoSorterAlgorithm.buildLinearChain(v2);
|
|
List<TopoSorterAlgorithm.Node> g3 = TopoSorterAlgorithm.buildLinearChain(v1);
|
|
List<TopoSorterAlgorithm.Node> g4 = TopoSorterAlgorithm.buildLinearChain(v2);
|
|
|
|
long def1 = TopoSorterAlgorithm.topoSortDefective(g1).comparisons;
|
|
long def2 = TopoSorterAlgorithm.topoSortDefective(g2).comparisons;
|
|
long fix1 = TopoSorterAlgorithm.topoSortFixed(g3).comparisons;
|
|
long fix2 = TopoSorterAlgorithm.topoSortFixed(g4).comparisons;
|
|
|
|
double defRatio = (double) def2 / def1;
|
|
double fixRatio = (double) fix2 / fix1;
|
|
|
|
System.out.printf(" V %d→%d: defective ratio=%.2f (expect ~4.0) fixed ratio=%.2f (expect 2.0)%n",
|
|
v1, v2, defRatio, fixRatio);
|
|
|
|
assertTrue("defective V=" + v1 + "→" + v2 + " ratio ≥ 3.8", defRatio >= 3.8);
|
|
// exact ratio = 2*(2V-1)/(V-1) → 4.22 at V=10, converges to 4.0
|
|
assertTrue("defective V=" + v1 + "→" + v2 + " ratio ≤ 4.25", defRatio <= 4.25);
|
|
assertTrue("fixed V=" + v1 + "→" + v2 + " 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++;
|
|
}
|
|
}
|
|
}
|