147 lines
5.5 KiB
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);
|
|
}
|
|
}
|