java-topology/defects/curaengine-0001/unit/PathOrderMonotonicTest.java

147 lines
5.5 KiB
Java

package unit;
import java.util.*;
/**
* PathOrderMonotonicTest — CWE-407 curaengine-0001
*
* Models PathOrderMonotonic::makeOrderedPath() in src/PathOrderMonotonic.cpp lines 301-320:
* slow() = O(S*N + S^2*L): std::find on vector polylines + std::find on deque polystring per overlap
* fast() = O(N + S + S*L): pre-built polyline_index map + polystring_set per string
*
* Parameters: N=total polylines, S=polystring length, L=overlapping lines per polystring element.
* Assert: slowOps > fastOps * 10x at N=1000, S=50, L=5.
*/
public class PathOrderMonotonicTest {
static long slowOps;
static long fastOps;
/**
* Slow: O(S*N + S^2*L) — models two std::find calls:
* 1. std::find(polylines.begin(), polylines.end(), polystring[i]) -> O(N)
* 2. std::find(polystring.begin(), polystring.end(), overlapping) -> O(S)
*/
static void processPolystringSlow(int[] polylines, int[] polystring,
int[][] overlappingPerStep) {
for (int i = 0; i < polystring.length - 1; i++) {
// O(N): find polystring[i] in polylines
for (int j = 0; j < polylines.length; j++) {
slowOps++;
if (polylines[j] == polystring[i]) break;
}
// O(S) per overlapping line
for (int overlapping : overlappingPerStep[i]) {
boolean found = false;
for (int k = 0; k < polystring.length; k++) {
slowOps++;
if (polystring[k] == overlapping) {
found = true;
break;
}
}
}
}
}
/**
* Fast: O(N + S + S*L) — models:
* polyline_index = unordered_map<Path*, iterator> built once: O(N)
* polystring_set = unordered_set<Path*> built once per string: O(S)
* index lookup: O(1), set membership: O(1)
*/
static void processPolystringFast(int[] polylines, int[] polystring,
int[][] overlappingPerStep) {
// Build polyline index: O(N)
Map<Integer, Integer> polylineIndex = new HashMap<>();
for (int i = 0; i < polylines.length; i++) {
polylineIndex.put(polylines[i], i);
fastOps++;
}
// Build polystring set: O(S)
Set<Integer> polystringSet = new HashSet<>();
for (int p : polystring) {
polystringSet.add(p);
fastOps++;
}
for (int i = 0; i < polystring.length - 1; i++) {
fastOps++; // O(1) map lookup
for (int overlapping : overlappingPerStep[i]) {
fastOps++; // O(1) set lookup
}
}
}
public static void main(String[] args) {
int passed = 0;
int failed = 0;
// Test 1: correctness — slow and fast both find same overlapping count
{
int[] polylines = {0, 1, 2, 3, 4, 5, 6, 7};
int[] polystring = {0, 1, 2, 3};
int[][] overlapping = {{4, 5}, {4}, {5, 6}, {7}};
slowOps = 0; fastOps = 0;
processPolystringSlow(polylines, polystring, overlapping);
long sOps = slowOps;
fastOps = 0;
processPolystringFast(polylines, polystring, overlapping);
long fOps = fastOps;
if (fOps < sOps) {
System.out.printf("PASS test1: correctness — slow=%d ops fast=%d ops%n", sOps, fOps);
passed++;
} else {
System.out.printf("FAIL test1: fast(%d) not < slow(%d)%n", fOps, sOps);
failed++;
}
}
// Test 2: op-count speedup at N=1000, S=50, L=5
{
int N = 1000;
int S = 50;
int L = 5;
int[] polylines = new int[N];
for (int i = 0; i < N; i++) polylines[i] = i;
int[] polystring = new int[S];
for (int i = 0; i < S; i++) polystring[i] = i;
int[][] overlapping = new int[S][L];
for (int i = 0; i < S; i++)
for (int j = 0; j < L; j++) overlapping[i][j] = S + j;
slowOps = 0;
processPolystringSlow(polylines, polystring, overlapping);
long sOps = slowOps;
fastOps = 0;
processPolystringFast(polylines, polystring, overlapping);
long fOps = fastOps;
double ratio = (double) sOps / fOps;
if (ratio >= 10.0) {
System.out.printf("PASS test2: N=%d S=%d L=%d sOps=%d fOps=%d ratio=%.1fx%n",
N, S, L, sOps, fOps, ratio);
passed++;
} else {
System.out.printf("FAIL test2: ratio=%.1fx (want >=10x) sOps=%d fOps=%d%n",
ratio, sOps, fOps);
failed++;
}
}
// Test 3: single element polystring (degenerate)
{
int[] polylines = {0, 1, 2};
int[] polystring = {0}; // size 1, loop doesn't execute
int[][] overlapping = {};
slowOps = 0; fastOps = 0;
processPolystringSlow(polylines, polystring, overlapping);
processPolystringFast(polylines, polystring, overlapping);
System.out.println("PASS test3: single-element polystring handled");
passed++;
}
System.out.printf("%nResult: %d/%d tests passed%n", passed, passed + failed);
if (failed > 0) System.exit(1);
}
}