178 lines
6.5 KiB
Java
178 lines
6.5 KiB
Java
import java.util.*;
|
|
|
|
/**
|
|
* Unit test for opencv-0001: QRDecode::divideIntoEvenSegments spline O(S²) defect.
|
|
*
|
|
* The defect: inside a loop over `num` (1..S/2) the function builds segment
|
|
* boundary points from spline_lines[i][idx] but then re-discovers those
|
|
* indices with std::find(spline_lines[i]...) — an O(S) scan per segment
|
|
* boundary. Total cost becomes O(S² * NUM_SIDES).
|
|
*
|
|
* The fix: record `idx` alongside the point so the measurement loop uses
|
|
* iterator + idx instead of std::find.
|
|
*
|
|
* This Java simulation models the two strategies and measures the operation
|
|
* count ratio to confirm the asymptotic difference.
|
|
*/
|
|
public class OpenCVTest {
|
|
|
|
// Simulate "spline line" as a list of float pairs (x,y)
|
|
static class Point2f {
|
|
float x, y;
|
|
Point2f(float x, float y) { this.x = x; this.y = y; }
|
|
@Override public boolean equals(Object o) {
|
|
if (!(o instanceof Point2f)) return false;
|
|
Point2f p = (Point2f) o;
|
|
return Float.compare(p.x, x) == 0 && Float.compare(p.y, y) == 0;
|
|
}
|
|
@Override public int hashCode() {
|
|
return Objects.hash(x, y);
|
|
}
|
|
}
|
|
|
|
/** Build a synthetic spline of length S. */
|
|
static List<Point2f> makeSpline(int S) {
|
|
List<Point2f> line = new ArrayList<>(S);
|
|
for (int i = 0; i < S; i++) {
|
|
line.add(new Point2f(i, (float)Math.sin(i * 0.1)));
|
|
}
|
|
return line;
|
|
}
|
|
|
|
/**
|
|
* DEFECTIVE: for each (num, side, segment) pair call List.indexOf() (= std::find)
|
|
* to locate the boundary point. Returns total operation count.
|
|
*/
|
|
static long defective(List<Point2f> spline, int maxNum) {
|
|
int S = spline.size();
|
|
long ops = 0;
|
|
List<Point2f> segPoints = new ArrayList<>();
|
|
for (int num = 1; num < maxNum; num++) {
|
|
segPoints.clear();
|
|
float step = (float) S / num;
|
|
for (int j = 0; j < num; j++) {
|
|
float val = j * step;
|
|
int idx = Math.round(val) >= S ? S - 1 : Math.round(val);
|
|
segPoints.add(spline.get(idx));
|
|
}
|
|
segPoints.add(spline.get(S - 1));
|
|
|
|
// Measurement: std::find equivalent for each boundary pair
|
|
for (int j = 0; j < segPoints.size() - 1; j++) {
|
|
Point2f start = segPoints.get(j);
|
|
Point2f end = segPoints.get(j + 1);
|
|
// O(S) scan each — the defect
|
|
int idxStart = spline.indexOf(start); ops += idxStart + 1;
|
|
int idxEnd = spline.indexOf(end); ops += idxEnd + 1;
|
|
}
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
/**
|
|
* FIXED: record indices alongside points; use direct index access.
|
|
* Returns total operation count.
|
|
*/
|
|
static long fixed(List<Point2f> spline, int maxNum) {
|
|
int S = spline.size();
|
|
long ops = 0;
|
|
List<Point2f> segPoints = new ArrayList<>();
|
|
List<Integer> segIdx = new ArrayList<>();
|
|
for (int num = 1; num < maxNum; num++) {
|
|
segPoints.clear(); segIdx.clear();
|
|
float step = (float) S / num;
|
|
for (int j = 0; j < num; j++) {
|
|
float val = j * step;
|
|
int idx = Math.round(val) >= S ? S - 1 : Math.round(val);
|
|
segPoints.add(spline.get(idx));
|
|
segIdx.add(idx);
|
|
}
|
|
segPoints.add(spline.get(S - 1));
|
|
segIdx.add(S - 1);
|
|
|
|
// Measurement: use recorded index — O(1)
|
|
for (int j = 0; j < segPoints.size() - 1; j++) {
|
|
int idxStart = segIdx.get(j); ops += 1; // O(1) access
|
|
int idxEnd = segIdx.get(j + 1); ops += 1;
|
|
}
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("opencv-0001: QRDecode::divideIntoEvenSegments O(S²) defect");
|
|
System.out.println("=============================================================");
|
|
|
|
int[] sizes = {50, 100, 200, 500};
|
|
|
|
boolean allPass = true;
|
|
for (int S : sizes) {
|
|
List<Point2f> spline = makeSpline(S);
|
|
int maxNum = S / 2;
|
|
|
|
long defOps = defective(spline, maxNum);
|
|
long fixOps = fixed(spline, maxNum);
|
|
double ratio = (double) defOps / fixOps;
|
|
|
|
System.out.printf("S=%4d maxNum=%3d defect_ops=%,12d fixed_ops=%,8d ratio=%.1fx%n",
|
|
S, maxNum, defOps, fixOps, ratio);
|
|
|
|
// Expect defect to be significantly worse (at least 10x at S=50)
|
|
if (ratio < 5.0) {
|
|
System.err.println(" FAIL: expected ratio >= 5.0 at S=" + S);
|
|
allPass = false;
|
|
}
|
|
}
|
|
|
|
// Verify correctness: both should cover same index ranges
|
|
{
|
|
int S = 100; int maxNum = 50;
|
|
List<Point2f> spline = makeSpline(S);
|
|
List<Integer> defIndices = new ArrayList<>();
|
|
List<Integer> fixIndices = new ArrayList<>();
|
|
|
|
// Run defective and collect start indices for num=10
|
|
{
|
|
List<Point2f> seg = new ArrayList<>();
|
|
float step = (float) S / 10;
|
|
for (int j = 0; j < 10; j++) {
|
|
float val = j * step;
|
|
int idx = Math.round(val) >= S ? S-1 : Math.round(val);
|
|
seg.add(spline.get(idx));
|
|
}
|
|
seg.add(spline.get(S - 1));
|
|
for (int j = 0; j < seg.size() - 1; j++) {
|
|
defIndices.add(spline.indexOf(seg.get(j)));
|
|
}
|
|
}
|
|
// Fixed version
|
|
{
|
|
List<Integer> si = new ArrayList<>();
|
|
float step = (float) S / 10;
|
|
for (int j = 0; j < 10; j++) {
|
|
float val = j * step;
|
|
int idx = Math.round(val) >= S ? S-1 : Math.round(val);
|
|
si.add(idx);
|
|
}
|
|
si.add(S - 1);
|
|
for (int j = 0; j < si.size() - 1; j++) {
|
|
fixIndices.add(si.get(j));
|
|
}
|
|
}
|
|
if (!defIndices.equals(fixIndices)) {
|
|
System.err.println(" FAIL: defect and fix produced different indices");
|
|
allPass = false;
|
|
} else {
|
|
System.out.println("Correctness check: PASS (indices match)");
|
|
}
|
|
}
|
|
|
|
if (allPass) {
|
|
System.out.println("ALL TESTS PASS");
|
|
System.exit(0);
|
|
} else {
|
|
System.out.println("SOME TESTS FAILED");
|
|
System.exit(1);
|
|
}
|
|
}
|
|
}
|