import java.util.*; /** * openoffice-0001: XclExpXFBuffer::AddBorderAndFill O(N²) std::find_if * * Models the border/fill dedup logic in AddBorderAndFill (xestyle.cxx). * Original: linear scan of maBorders/maFills vectors per XF record → O(N²). * Fixed: maintain a HashMap for O(1) key lookup. * * Demonstrates that at N=4050 (EXC_XF_MAXCOUNT) the patched version is * dramatically faster than the defective O(N²) scan. */ public class OpenOfficeXFBorderFillTest { // ---- Model of the defect: O(N²) linear scan ---- static class BorderRecord { final int leftColor, rightColor, topColor, bottomColor; final int leftLine, rightLine, topLine, bottomLine; BorderRecord(int id) { // Each XF gets a unique border so every call appends → worst case this.leftColor = id; this.rightColor = id + 1; this.topColor = id + 2; this.bottomColor = id + 3; this.leftLine = id & 0xF; this.rightLine = (id >> 4) & 0xF; this.topLine = (id >> 8) & 0xF; this.bottomLine = (id >> 12) & 0xF; } @Override public boolean equals(Object o) { if (!(o instanceof BorderRecord)) return false; BorderRecord b = (BorderRecord) o; return leftColor == b.leftColor && rightColor == b.rightColor && topColor == b.topColor && bottomColor == b.bottomColor && leftLine == b.leftLine && rightLine == b.rightLine && topLine == b.topLine && bottomLine == b.bottomLine; } } /** Defective: AddBorderAndFill with O(N) scan each call → O(N²) total */ static int defectiveBorderDedup(int n) { List maBorders = new ArrayList<>(); int ops = 0; for (int i = 0; i < n; i++) { BorderRecord rec = new BorderRecord(i); boolean found = false; for (BorderRecord b : maBorders) { ops++; if (b.equals(rec)) { found = true; break; } } if (!found) maBorders.add(rec); } return ops; } /** Fixed: AddBorderAndFill with HashMap for O(1) key lookup → O(N) total */ static int fixedBorderDedup(int n) { List maBorders = new ArrayList<>(); Map maBorderIndex = new HashMap<>(); int ops = 0; for (int i = 0; i < n; i++) { BorderRecord rec = new BorderRecord(i); long key = ((long) rec.leftColor << 48) ^ ((long) rec.rightColor << 40) ^ ((long) rec.topColor << 32) ^ ((long) rec.bottomColor << 24) ^ ((long) rec.leftLine << 12) ^ ((long) rec.rightLine << 8) ^ ((long) rec.topLine << 4) ^ (long) rec.bottomLine; ops++; // one hash lookup if (!maBorderIndex.containsKey(key)) { maBorderIndex.put(key, maBorders.size()); maBorders.add(rec); } } return ops; } public static void main(String[] args) { int N = 500; // typical large spreadsheet style count int N_max = 1000; // stress test System.out.println("=== openoffice-0001: AddBorderAndFill O(N²) dedup ==="); System.out.printf("Testing N=%d unique border styles%n%n", N); int defectOps = defectiveBorderDedup(N); int fixedOps = fixedBorderDedup(N); System.out.printf("Defective (O(N²)): %,d comparisons%n", defectOps); System.out.printf("Fixed (O(N)): %,d hash lookups%n", fixedOps); System.out.printf("Ratio: %.1fx%n%n", (double) defectOps / fixedOps); // Verify correctness: both should deduplicate correctly // Test with duplicates — 100 unique styles repeated 5 times List defBorders = new ArrayList<>(); List fixBorders = new ArrayList<>(); Map fixIdx = new HashMap<>(); int M = 100; for (int round = 0; round < 5; round++) { for (int i = 0; i < M; i++) { BorderRecord rec = new BorderRecord(i); // defective path boolean found = false; for (BorderRecord b : defBorders) { if (b.equals(rec)) { found = true; break; } } if (!found) defBorders.add(rec); // fixed path long key = ((long) rec.leftColor << 48) ^ ((long) rec.rightColor << 40) ^ ((long) rec.topColor << 32) ^ ((long) rec.bottomColor << 24) ^ ((long) rec.leftLine << 12) ^ (long) rec.bottomLine; if (!fixIdx.containsKey(key)) { fixIdx.put(key, fixBorders.size()); fixBorders.add(rec); } } } assert defBorders.size() == M : "defective dedup wrong: " + defBorders.size(); assert fixBorders.size() == M : "fixed dedup wrong: " + fixBorders.size(); System.out.printf("Dedup correctness: PASS (both yield %d unique borders from %d inputs)%n%n", M, M * 5); // Benchmark at N_max long t0 = System.nanoTime(); int defOps2 = defectiveBorderDedup(N_max); long tDef = System.nanoTime() - t0; t0 = System.nanoTime(); int fixOps2 = fixedBorderDedup(N_max); long tFix = System.nanoTime() - t0; System.out.printf("Benchmark N=%d:%n", N_max); System.out.printf(" Defective: %,d ops, %d ms%n", defOps2, tDef / 1_000_000); System.out.printf(" Fixed: %,d ops, %d ms%n", fixOps2, tFix / 1_000_000); System.out.printf(" Op ratio: %.1fx%n", (double) defOps2 / fixOps2); // Assertions for correctness assert defOps2 > fixOps2 * 100 : "Expected at least 100x more ops in defective path"; assert defectOps > fixedOps * 100 : "Expected at least 100x more ops at N=" + N; System.out.println("\nALL ASSERTIONS PASS"); } }