import java.util.*; /** * Unit test for calligra-0001: KoShapeManager addShape O(N^2) membership check. * * Defect: libs/flake/KoShapeManager.cpp addShape() * if (d->shapes.contains(shape)) // QList::contains is O(N) * d->shapes.append(shape); * * Called from setShapes() in a loop over N shapes, producing O(N^2) total. * * Fix: change d->shapes from QList to QSet, making contains() O(1). * * Complexity: O(N^2) -> O(N). */ public class CalligraShapeManagerAddTest { // --- Defective implementation: QList equivalent --- static List buildShapeListLinear(int[] shapes) { List result = new ArrayList<>(); for (int shape : shapes) { if (!result.contains(shape)) { // O(N) each result.add(shape); } } return result; } // --- Fixed implementation: QSet equivalent --- static Set buildShapeSetFixed(int[] shapes) { Set result = new LinkedHashSet<>(); for (int shape : shapes) { result.add(shape); // O(1) each, set deduplicates automatically } return result; } // --- Benchmark --- static long benchmarkLinear(int N) { int[] shapes = new int[N]; for (int i = 0; i < N; i++) shapes[i] = i; // all unique -> worst case long start = System.nanoTime(); buildShapeListLinear(shapes); return System.nanoTime() - start; } static long benchmarkFixed(int N) { int[] shapes = new int[N]; for (int i = 0; i < N; i++) shapes[i] = i; long start = System.nanoTime(); buildShapeSetFixed(shapes); return System.nanoTime() - start; } public static void main(String[] args) { System.out.println("=== calligra-0001: KoShapeManager addShape dedup O(N^2) ==="); // Correctness: unique shapes { int[] shapes = {10, 20, 30, 40, 50}; List linear = buildShapeListLinear(shapes); Set fixed = buildShapeSetFixed(shapes); if (!new HashSet<>(linear).equals(fixed)) { System.err.println("FAIL: unique-shape mismatch linear=" + linear + " fixed=" + fixed); System.exit(1); } System.out.println("PASS: unique shapes, result size=" + fixed.size()); } // Correctness: duplicate shapes (setShapes called with duplicates) { int[] shapes = {1, 2, 3, 2, 1, 4}; List linear = buildShapeListLinear(shapes); Set fixed = buildShapeSetFixed(shapes); if (!new HashSet<>(linear).equals(fixed)) { System.err.println("FAIL: duplicate-shape mismatch linear=" + linear + " fixed=" + fixed); System.exit(1); } if (linear.size() != 4 || fixed.size() != 4) { System.err.println("FAIL: expected 4 unique shapes, linear=" + linear.size() + " fixed=" + fixed.size()); System.exit(1); } System.out.println("PASS: duplicate shapes deduped correctly, result size=" + fixed.size()); } // Performance test int N = 5000; // Warmup for (int w = 0; w < 3; w++) { benchmarkLinear(N/10); benchmarkFixed(N/10); } long linearNs = benchmarkLinear(N); long fixedNs = benchmarkFixed(N); double ratio = (double) linearNs / fixedNs; System.out.printf("Linear N=%d: %,d ns%n", N, linearNs); System.out.printf("Fixed N=%d: %,d ns%n", N, fixedNs); System.out.printf("Speedup: %.1fx%n", ratio); if (ratio < 5.0) { System.err.printf("FAIL: expected >= 5x speedup at N=%d, got %.1fx%n", N, ratio); System.exit(1); } System.out.println("PASS: speedup >= 5x"); System.out.println("PASS: all tests passed"); } }