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 makeSpline(int S) { List 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 spline, int maxNum) { int S = spline.size(); long ops = 0; List 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 spline, int maxNum) { int S = spline.size(); long ops = 0; List segPoints = new ArrayList<>(); List 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 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 spline = makeSpline(S); List defIndices = new ArrayList<>(); List fixIndices = new ArrayList<>(); // Run defective and collect start indices for num=10 { List 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 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); } } }