java-topology/defects/freecad-0003/unit/FreeCadCrossSectionTest.java
russell@unturf.com 3bf7ed2cec freecad+kicad: 5-MOAD scan; 3 CWE-407 defects, MOAD 0002-0005 CLEAN
freecad-0003: CrossSection::removeDuplicates O(W^2*E) wire dedup via
std::find_if over growing list; fix: hash-keyed seen-set O(W*E log E).
Severity MEDIUM-HIGH, ~200x at W=400 duplicate wires.

freecad-0004: SketchAnalysis::detectMissingEqualityConstraints O(C*M^2)
std::find_if over std::list of equal-line/radius candidates; fix:
unordered_multimap keyed on canonical (GeoId,GeoId) pair, O(C).
Severity MEDIUM-HIGH, ~5x at C=50/M=50, grows with M^2.

kicad-0003: BOARD_NETLIST_UPDATER::testConnectivity calls FindPadByNumber
(O(P) linear scan) for each of N pins per footprint; fix: build
unordered_map<padNumber,PAD*> once per footprint, O(P+N) total.
Severity HIGH, ~19x at P=256 pads, ~512x at P=512 pads.

MOAD-0002/0003/0004/0005: CLEAN for both projects (documented in SCAN-NOTES).
2026-03-31 22:09:45 -04:00

80 lines
3.1 KiB
Java

import java.util.*;
/**
* Java model of FreeCAD freecad-0003: CrossSection::removeDuplicates O(W^2 * E)
* vs O(W * E log E) with a hash-keyed seen-set.
*
* Each "wire" is modelled as a sorted list of long edge-IDs (stand-ins for
* TopoDS_TShape pointers). Two wires with the same edge-ID set are duplicates.
*/
public class FreeCadCrossSectionTest {
// --- Defect: O(W^2 * E) list-based dedup --------------------------------
static List<List<Long>> removeDuplicatesDefect(List<List<Long>> wires) {
List<List<Long>> result = new ArrayList<>();
for (List<Long> wire : wires) {
boolean dup = false;
for (List<Long> kept : result) { // O(W) scan
if (kept.size() == wire.size()) {
boolean same = true;
for (int i = 0; i < kept.size(); i++) { // O(E)
if (!kept.get(i).equals(wire.get(i))) { same = false; break; }
}
if (same) { dup = true; break; }
}
}
if (!dup) result.add(wire);
}
return result;
}
// --- Fix: O(W * E) hash-keyed seen-set -----------------------------------
static List<List<Long>> removeDuplicatesFixed(List<List<Long>> wires) {
Set<List<Long>> seen = new HashSet<>();
List<List<Long>> result = new ArrayList<>();
for (List<Long> wire : wires) {
List<Long> key = new ArrayList<>(wire);
Collections.sort(key); // O(E log E) canonical key
if (seen.add(key)) // O(1) set membership
result.add(wire);
}
return result;
}
// --- Benchmark -----------------------------------------------------------
public static void main(String[] args) {
Random rng = new Random(42);
for (int W : new int[]{50, 100, 200, 400}) {
int E = 8; // edges per wire
// Build W/2 unique wires, each duplicated once
List<List<Long>> wires = new ArrayList<>();
for (int w = 0; w < W / 2; w++) {
List<Long> wire = new ArrayList<>();
for (int e = 0; e < E; e++) wire.add((long)(w * 100 + e));
Collections.sort(wire);
wires.add(wire);
wires.add(new ArrayList<>(wire)); // duplicate
}
Collections.shuffle(wires, rng);
long t0 = System.nanoTime();
List<List<Long>> resD = removeDuplicatesDefect(wires);
long defectNs = System.nanoTime() - t0;
long t1 = System.nanoTime();
List<List<Long>> resF = removeDuplicatesFixed(wires);
long fixedNs = System.nanoTime() - t1;
assert resD.size() == resF.size()
: "Unique count mismatch: " + resD.size() + " vs " + resF.size();
double ratio = (double) defectNs / Math.max(fixedNs, 1);
System.out.printf("W=%4d E=%d defect=%8dns fixed=%8dns ratio=%.1fx%n",
W, E, defectNs, fixedNs, ratio);
}
System.out.println("PASS");
}
}