java-topology/defects/kicad/unit/KicadZoneFillerAlgorithmTest.java

126 lines
4.4 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.*;
/**
* Models KiCad ZONE_FILLER::Fill() iterative refill dedup.
*
* SLOW: O(Z_r × Z × L × Z×L) — std::find on growing vector per (zone, layer) pair.
* FAST: O(Z_r × Z × L) — HashSet dedup, O(1) insert/contains.
*
* CWE-407: pcbnew/zone_filler.cpp:998 (ZONE_FILLER::Fill iterative refill path)
*/
public class KicadZoneFillerAlgorithmTest {
record ZoneLayer(int zoneId, int layerId) {}
// -------------------------------------------------------------------------
// Slow — std::find O(N) scan on growing list
// -------------------------------------------------------------------------
static class SlowZoneFiller {
long cmpOps = 0;
boolean find(List<ZoneLayer> list, ZoneLayer item) {
for (ZoneLayer zl : list) {
cmpOps++;
if (zl.equals(item)) return true;
}
return false;
}
List<ZoneLayer> getZonesToRefill(int zRemoved, int zTotal, int layers) {
List<ZoneLayer> zonesToRefill = new ArrayList<>();
for (int r = 0; r < zRemoved; r++) {
for (int z = 0; z < zTotal; z++) {
for (int l = 0; l < layers; l++) {
ZoneLayer item = new ZoneLayer(z, l);
if (!find(zonesToRefill, item)) {
zonesToRefill.add(item);
}
}
}
}
return zonesToRefill;
}
long ops(int zRemoved, int zTotal, int layers) {
cmpOps = 0;
getZonesToRefill(zRemoved, zTotal, layers);
return cmpOps;
}
}
// -------------------------------------------------------------------------
// Fast — HashSet O(1) dedup
// -------------------------------------------------------------------------
static class FastZoneFiller {
long cmpOps = 0;
List<ZoneLayer> getZonesToRefill(int zRemoved, int zTotal, int layers) {
Set<ZoneLayer> seen = new HashSet<>();
List<ZoneLayer> zonesToRefill = new ArrayList<>();
for (int r = 0; r < zRemoved; r++) {
for (int z = 0; z < zTotal; z++) {
for (int l = 0; l < layers; l++) {
ZoneLayer item = new ZoneLayer(z, l);
cmpOps++;
if (seen.add(item)) {
zonesToRefill.add(item);
}
}
}
}
return zonesToRefill;
}
long ops(int zRemoved, int zTotal, int layers) {
cmpOps = 0;
getZonesToRefill(zRemoved, zTotal, layers);
return cmpOps;
}
}
public static void main(String[] args) {
SlowZoneFiller slow = new SlowZoneFiller();
FastZoneFiller fast = new FastZoneFiller();
int passed = 0, total = 0;
System.out.println("=== kicad-0002: zone_filler std::find O(Z²×L²) ===");
// (zRemoved, zTotal, layers)
int[][] configs = {
{5, 20, 2}, // small board
{10, 50, 4}, // realistic (agent's estimate)
{15, 100, 6}, // large board
};
for (int[] cfg : configs) {
int zr = cfg[0], z = cfg[1], l = cfg[2];
long s = slow.ops(zr, z, l);
long f = fast.ops(zr, z, l);
double ratio = (double) s / Math.max(f, 1);
total++;
boolean ok = s > f && ratio >= 5.0;
System.out.printf("Zr=%2d Z=%3d L=%d slow=%9d fast=%6d ratio=%6.1fx %s%n",
zr, z, l, s, f, ratio, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Correctness: both produce same set of zones to refill
SlowZoneFiller s2 = new SlowZoneFiller();
FastZoneFiller f2 = new FastZoneFiller();
List<ZoneLayer> sr = s2.getZonesToRefill(10, 50, 4);
List<ZoneLayer> fr = f2.getZonesToRefill(10, 50, 4);
Set<ZoneLayer> ss = new HashSet<>(sr), fs = new HashSet<>(fr);
total++;
boolean correct = ss.equals(fs);
System.out.printf("correctness (%d zones to refill): %s%n", sr.size(),
correct ? "PASS" : "FAIL");
if (correct) passed++;
System.out.printf("%n%d/%d PASS%n", passed, total);
if (passed < total) System.exit(1);
}
}