145 lines
5.7 KiB
Java
145 lines
5.7 KiB
Java
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);
|
||
}
|
||
}
|