import java.util.*; /** * CWE-407 simulation: Panda3D NodePathCollection O(N²) membership scan. * * Defect: panda3d-0001 * File: panda/src/pgraph/nodePathCollection.cxx * * remove_duplicate_paths() uses a nested O(N²) loop to check for duplicates: * for i in 0..N: * for j in 0..i: * if path[i] == path[j]: duplicated = true * * remove_paths_from() calls has_path() (O(N) scan) for each of N paths → O(N²). * * Fix: use a HashSet for O(1) membership checks → O(N) total. */ public class Panda3DTest { /** Simulates the defective O(N^2) remove_duplicate_paths(). */ static List removeDuplicatesDefective(List paths) { List result = new ArrayList<>(); int n = paths.size(); for (int i = 0; i < n; i++) { boolean duplicated = false; for (int j = 0; j < i && !duplicated; j++) { duplicated = paths.get(i).equals(paths.get(j)); } if (!duplicated) { result.add(paths.get(i)); } } return result; } /** Simulates the fixed O(N log N) remove_duplicate_paths() using a set. */ static List removeDuplicatesFixed(List paths) { List result = new ArrayList<>(); Set seen = new LinkedHashSet<>(); for (Integer p : paths) { if (seen.add(p)) { result.add(p); } } return result; } /** Simulates the defective O(N*M) remove_paths_from(). */ static List removePathsFromDefective(List self, List other) { List result = new ArrayList<>(); for (Integer p : self) { // has_path() is O(M) linear scan if (!other.contains(p)) { result.add(p); } } return result; } /** Simulates the fixed O(N+M) remove_paths_from() using a set. */ static List removePathsFromFixed(List self, List other) { Set otherSet = new HashSet<>(other); List result = new ArrayList<>(); for (Integer p : self) { if (!otherSet.contains(p)) { result.add(p); } } return result; } /** Count O(N^2) operations for defective remove_duplicate_paths. */ static long opsDefectiveDeduplicate(int n) { long ops = 0; for (int i = 0; i < n; i++) { ops += i; // inner loop runs i times } return ops; } /** Count O(N) operations for fixed remove_duplicate_paths. */ static long opsFixedDeduplicate(int n) { return n; // one hash-insert per element } public static void main(String[] args) { // --- Correctness tests --- List paths = Arrays.asList(1, 2, 3, 2, 4, 1, 5); List defectResult = removeDuplicatesDefective(paths); List fixedResult = removeDuplicatesFixed(paths); assert defectResult.equals(fixedResult) : "removeDuplicates: results differ: " + defectResult + " vs " + fixedResult; List allPaths = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8); List toRemove = Arrays.asList(2, 4, 6); List defectRemove = removePathsFromDefective(allPaths, toRemove); List fixedRemove = removePathsFromFixed(allPaths, toRemove); assert defectRemove.equals(fixedRemove) : "removePathsFrom: results differ: " + defectRemove + " vs " + fixedRemove; System.out.println("PASS: correctness verified for remove_duplicate_paths and remove_paths_from"); // --- Complexity ratio test --- int[] sizes = {100, 500, 1000}; System.out.println("\nOperation count ratio (defective / fixed) for remove_duplicate_paths:"); for (int n : sizes) { long defectOps = opsDefectiveDeduplicate(n); long fixedOps = opsFixedDeduplicate(n); double ratio = (double) defectOps / fixedOps; System.out.printf(" N=%4d defect=%6d fixed=%4d ratio=%.1fx%n", n, defectOps, fixedOps, ratio); assert ratio > 10.0 : "Expected ratio > 10x at N=" + n + ", got " + ratio; } // --- Wall-clock timing test --- int N = 2000; // Build a list with many duplicates (every path repeated 4 times) List bigList = new ArrayList<>(N); for (int i = 0; i < N; i++) { bigList.add(i % (N / 4)); } long t0 = System.nanoTime(); removeDuplicatesDefective(bigList); long defectNs = System.nanoTime() - t0; t0 = System.nanoTime(); removeDuplicatesFixed(bigList); long fixedNs = System.nanoTime() - t0; double wallRatio = (double) defectNs / Math.max(fixedNs, 1); System.out.printf("%nWall-clock timing for N=%d remove_duplicate_paths:%n", N); System.out.printf(" defective: %6.2f ms%n", defectNs / 1e6); System.out.printf(" fixed: %6.2f ms%n", fixedNs / 1e6); System.out.printf(" ratio: %.1fx%n", wallRatio); assert wallRatio > 2.0 : "Expected wall-clock ratio > 2x at N=" + N + ", got " + wallRatio; System.out.println("\nPASS: all assertions passed"); } }