java-topology/defects/openoffice-0001/test/OpenOfficeXFBorderFillTest.java

147 lines
6.1 KiB
Java

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<Long, Integer> 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<BorderRecord> 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<BorderRecord> maBorders = new ArrayList<>();
Map<Long, Integer> 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<BorderRecord> defBorders = new ArrayList<>();
List<BorderRecord> fixBorders = new ArrayList<>();
Map<Long, Integer> 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");
}
}