java-topology/defects/solvespace-0001/unit/SolvespaceArcConstraintScanTest.java

145 lines
5.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<Arc> arcs, List<Constraint> 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<Arc> arcs, List<Constraint> constraints) {
long ops = 0;
Set<Long> 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<Constraint> makeConstraints(int group, int total) {
List<Constraint> 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<Arc> makeArcs(int group, int n) {
List<Arc> 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<Constraint> constraints = makeConstraints(GROUP, 20);
List<Arc> 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<Constraint> constraints = new ArrayList<>();
List<Arc> 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<Constraint> constraints = makeConstraints(GROUP, C);
List<Arc> 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);
}
}