import java.util.*; /** * Unit test for cmake-0007: TargetRuntimeDllDirsNode O(D^2) -> O(D) dedup. * * Models $ evaluator: collect unique DLL directories * from a list of DLL paths, preserving insertion order. * * Defect: std::find over growing dllDirs vector = O(D) per entry = O(D^2) total. * Fix: parallel unordered_set for membership + vector for order = O(1) per entry. */ public class DllDirsAlgorithm { static String getFilenamePath(String path) { int slash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')); return slash >= 0 ? path.substring(0, slash) : "."; } // --- Defective: O(D^2) --- static List collectDllDirsDefective(List dlls) { List dllDirs = new ArrayList<>(); for (String dll : dlls) { String dir = getFilenamePath(dll); if (!dllDirs.contains(dir)) { // O(D) linear scan -- the defect dllDirs.add(dir); } } return dllDirs; } // --- Fixed: O(D) --- static List collectDllDirsFixed(List dlls) { List dllDirs = new ArrayList<>(); Set dllDirsSet = new HashSet<>(); for (String dll : dlls) { String dir = getFilenamePath(dll); if (dllDirsSet.add(dir)) { // O(1) hash insert+check dllDirs.add(dir); } } return dllDirs; } static List makeDlls(int numDirs, int dllsPerDir) { List dlls = new ArrayList<>(); for (int d = 0; d < numDirs; d++) { for (int i = 0; i < dllsPerDir; i++) { dlls.add("C:/Qt/" + d + "/lib" + i + ".dll"); } } return dlls; } static void testCorrectness() { List dlls = Arrays.asList( "C:/Qt/bin/Qt6Core.dll", "C:/Qt/bin/Qt6Gui.dll", "C:/Boost/lib/boost_system.dll", "C:/Qt/bin/Qt6Widgets.dll", "C:/Boost/lib/boost_thread.dll" ); List defect = collectDllDirsDefective(dlls); List fixed = collectDllDirsFixed(dlls); if (!defect.equals(fixed)) { throw new AssertionError("Results differ: " + defect + " vs " + fixed); } if (defect.size() != 2) { throw new AssertionError("Expected 2 unique dirs, got " + defect.size() + ": " + defect); } // Order must be preserved (insertion order) if (!defect.get(0).equals("C:/Qt/bin") || !defect.get(1).equals("C:/Boost/lib")) { throw new AssertionError("Wrong order: " + defect); } System.out.println("PASS testCorrectness (2 unique dirs, insertion order preserved)"); } static void testAllUnique() { List dlls = Arrays.asList( "C:/dir1/a.dll", "C:/dir2/b.dll", "C:/dir3/c.dll" ); List result = collectDllDirsFixed(dlls); if (result.size() != 3) throw new AssertionError("Expected 3: " + result); System.out.println("PASS testAllUnique"); } static void testAllSameDir() { List dlls = Arrays.asList( "C:/Qt/bin/A.dll", "C:/Qt/bin/B.dll", "C:/Qt/bin/C.dll" ); List result = collectDllDirsFixed(dlls); if (result.size() != 1) throw new AssertionError("Expected 1: " + result); if (!result.get(0).equals("C:/Qt/bin")) throw new AssertionError("Wrong dir: " + result); System.out.println("PASS testAllSameDir"); } static void testBenchmark() { int NUM_DIRS = 20; int DLLS_PER_DIR = 5; // 100 DLLs total, 20 unique dirs List dlls = makeDlls(NUM_DIRS, DLLS_PER_DIR); int D = dlls.size(); // Count defective ops (linear scans in contains) long defectiveOps = 0; List seen = new ArrayList<>(); for (String dll : dlls) { String dir = getFilenamePath(dll); for (String s : seen) { defectiveOps++; if (s.equals(dir)) break; } if (!seen.contains(dir)) seen.add(dir); } long fixedOps = D; // one hash lookup per dll double ratio = (double) defectiveOps / fixedOps; System.out.printf("BENCH dllDirs D=%d dirs=%d: defective=%d ops, fixed=%d ops, ratio=%.1fx%n", D, NUM_DIRS, defectiveOps, fixedOps, ratio); if (ratio < 2.0) { throw new AssertionError("Expected ratio >= 2x, got " + ratio); } System.out.println("PASS testBenchmark (ratio >= 2x confirmed)"); } public static void main(String[] args) { testCorrectness(); testAllUnique(); testAllSameDir(); testBenchmark(); System.out.println("ALL PASS cmake-0007 DllDirs"); } }