package unit; import java.util.*; /** * Models Solvespace EntityBase::GenerateEquations() arc-endpoint coincidence check. * * Defect: std::find_if over all constraints per arc entity in outer loop — O(A×C). * Fix: build Set of coincident endpoint pairs once per group — O(C) + O(A) lookups. * * solvespace-0001 MOAD-0001 CWE-407 * No JUnit — compile and run standalone. */ public class SolvespaceArcConstraintScanTest { static final int POINTS_COINCIDENT = 1; static final int DISTANCE = 2; record Constraint(int type, int group, int ptA, int ptB) {} record Arc(int group, int ptStart, int ptEnd) {} // ------------------------------------------------------------------- // DEFECT: O(A × C) — full constraint scan per arc entity // ------------------------------------------------------------------- static long slowGenerate(List arcs, List constraints) { long ops = 0; for (Arc arc : arcs) { for (Constraint con : constraints) { // O(C) per arc ops++; if (con.group() != arc.group()) continue; if (con.type() != POINTS_COINCIDENT) continue; if ((con.ptA() == arc.ptStart() && con.ptB() == arc.ptEnd()) || (con.ptA() == arc.ptEnd() && con.ptB() == arc.ptStart())) { break; // found coincident — arc is closed, skip equation } } } return ops; } // ------------------------------------------------------------------- // FIX: O(C) build + O(A) lookups — hash set of packed endpoint pairs // ------------------------------------------------------------------- static long fastGenerate(List arcs, List constraints) { long ops = 0; Set coincident = new HashSet<>(); int lastGroup = -1; for (Arc arc : arcs) { if (arc.group() != lastGroup) { coincident.clear(); lastGroup = arc.group(); for (Constraint con : constraints) { ops++; if (con.group() != arc.group()) continue; if (con.type() != POINTS_COINCIDENT) continue; long ab = ((long) con.ptA() << 32) | (con.ptB() & 0xFFFFFFFFL); long ba = ((long) con.ptB() << 32) | (con.ptA() & 0xFFFFFFFFL); coincident.add(ab); coincident.add(ba); } } long key = ((long) arc.ptStart() << 32) | (arc.ptEnd() & 0xFFFFFFFFL); ops++; // O(1) set lookup // coincident.contains(key) determines if arc needs equation } return ops; } static List makeConstraints(int group, int total) { List list = new ArrayList<>(); for (int i = 0; i < total / 4; i++) { list.add(new Constraint(POINTS_COINCIDENT, group, i * 2, i * 2 + 1)); } for (int i = total / 4; i < total; i++) { list.add(new Constraint(DISTANCE, group, i, i + 1)); } return list; } static List makeArcs(int group, int n) { List list = new ArrayList<>(); for (int i = 0; i < n; i++) { int ptStart = i * 2; // Even arcs are closed (coincident with a constraint), odd arcs are open int ptEnd = i % 2 == 0 ? (i * 2 + 1) : (1000 + i); list.add(new Arc(group, ptStart, ptEnd)); } return list; } public static void main(String[] args) { int tests = 0, passed = 0; // --- correctness: small sketch --- tests++; { int GROUP = 1; List constraints = makeConstraints(GROUP, 20); List arcs = makeArcs(GROUP, 10); long slowOps = slowGenerate(arcs, constraints); long fastOps = fastGenerate(arcs, constraints); boolean ok = slowOps >= fastOps; System.out.printf("%s correctness-small A=10 C=20 slowOps=%d fastOps=%d%n", ok ? "PASS" : "FAIL", slowOps, fastOps); if (ok) passed++; } // --- correctness: no constraints --- tests++; { int GROUP = 1; List constraints = new ArrayList<>(); List arcs = makeArcs(GROUP, 20); long slowOps = slowGenerate(arcs, constraints); long fastOps = fastGenerate(arcs, constraints); boolean ok = slowOps >= 0 && fastOps >= 0; System.out.printf("%s correctness-no-constraints A=20 C=0 slowOps=%d fastOps=%d%n", ok ? "PASS" : "FAIL", slowOps, fastOps); if (ok) passed++; } // --- speedup benchmarks --- int[][] benchSizes = {{20, 80}, {50, 200}, {100, 400}, {200, 800}}; for (int[] sz : benchSizes) { tests++; int A = sz[0], C = sz[1]; int GROUP = 1; List constraints = makeConstraints(GROUP, C); List arcs = makeArcs(GROUP, A); long slowOps = slowGenerate(arcs, constraints); long fastOps = fastGenerate(arcs, constraints); double ratio = (double) slowOps / fastOps; // At A=200, C=800: slow ≈ 200*800 = 160,000; fast ≈ 800+200 = 1,000 → 160x boolean ok = ratio >= 5.0; System.out.printf("%s speedup A=%d C=%d slowOps=%d fastOps=%d ratio=%.1fx%n", ok ? "PASS" : "FAIL", A, C, slowOps, fastOps, ratio); if (ok) passed++; } System.out.println("\n" + passed + "/" + tests + " PASS"); if (passed != tests) System.exit(1); } }