package unit; import java.util.*; /** * Unit test for dgraph-0001: CWE-407 O(P) route.indexOf inside k-shortest-path neighbour loop. * * Slow path: ArrayList path, indexOf = O(P) linear scan. * Fast path: HashMap visited alongside path, lookup = O(1). * * Both produce identical cycle-detection results; the test confirms op counts diverge as N grows. */ public class DgraphTest { // ------------------------------------------------------------------------- // Slow path: simulates route.indexOf — linear scan of path slice // ------------------------------------------------------------------------- static long slowIndexOf(List path, long uid) { for (long val : path) { if (val == uid) return 0; // found } return -1; } /** * Simulate k-shortest-paths neighbour expansion with O(P) cycle detection. * Returns total indexOf scan operations performed. * * Graph: linear chain 0→1→2→...→(n-1) plus a back-edge n-1→0 to force cycle checks. * Each dequeue: process n neighbours, call indexOf once per neighbour. * Dequeue n items (one per node). Path grows by 1 per hop. */ static long slowOps(int n) { long ops = 0; // Simulate dequeuing n items with path lengths 1..n for (int hop = 1; hop <= n; hop++) { // path length at this hop = hop int pathLen = hop; // process n neighbours per dequeue for (int nb = 0; nb < n; nb++) { // indexOf scans entire path ops += pathLen; } } return ops; } // ------------------------------------------------------------------------- // Fast path: O(1) HashMap lookup for cycle detection // ------------------------------------------------------------------------- static long fastOps(int n) { long ops = 0; // Simulate dequeuing n items with path lengths 1..n for (int hop = 1; hop <= n; hop++) { // n neighbours per dequeue, each is O(1) map lookup = 1 op for (int nb = 0; nb < n; nb++) { ops += 1; } } return ops; } // ------------------------------------------------------------------------- // Correctness check: both strategies agree on cycle detection result // ------------------------------------------------------------------------- static boolean slowContains(List path, long uid) { return slowIndexOf(path, uid) != -1; } static boolean fastContains(Map visited, long uid) { return visited.containsKey(uid); } static void testCorrectness() { List path = new ArrayList<>(Arrays.asList(10L, 20L, 30L, 40L)); Map visited = new HashMap<>(); for (long v : path) visited.put(v, Boolean.TRUE); // uid present in path assert slowContains(path, 30L) == fastContains(visited, 30L) : "FAIL: mismatch for uid in path"; // uid absent from path assert slowContains(path, 99L) == fastContains(visited, 99L) : "FAIL: mismatch for uid not in path"; System.out.println("1/2 PASS correctness: slow and fast agree on cycle detection"); } // ------------------------------------------------------------------------- // Complexity check: slow grows O(n²), fast grows O(n) // ------------------------------------------------------------------------- static void testComplexity() { int n1 = 100; int n2 = 1000; long slowN1 = slowOps(n1); long slowN2 = slowOps(n2); long fastN1 = fastOps(n1); long fastN2 = fastOps(n2); // Slow should grow ~100x (10x nodes → 10x*10x = 100x ops) double slowRatio = (double) slowN2 / slowN1; // Fast should grow ~10x (linear in n²: n*n neighbours each 1 op) // Actually fast is also n*n ops but each is 1, while slow is n*n*pathLen // The ratio slowN2/fastN2 should be >> 1 at n2 (slow pays pathLen = n per op) double overhead = (double) slowN2 / fastN2; System.out.printf(" slow ops n=%d: %,d%n", n1, slowN1); System.out.printf(" slow ops n=%d: %,d%n", n2, slowN2); System.out.printf(" fast ops n=%d: %,d%n", n1, fastN1); System.out.printf(" fast ops n=%d: %,d%n", n2, fastN2); System.out.printf(" slow/fast overhead at n=%d: %.1fx%n", n2, overhead); // slow is O(n³) here: n hops * n neighbours * n path length // fast is O(n²): n hops * n neighbours * 1 // overhead should be ~n2 = 1000 assert slowRatio > 50.0 : "FAIL: slow ops did not grow super-linearly: ratio=" + slowRatio; assert overhead > 100.0 : "FAIL: slow not significantly worse than fast: overhead=" + overhead; System.out.printf("2/2 PASS complexity: slow/fast=%.0fx (expected ~%d)%n", overhead, n2); } public static void main(String[] args) { System.out.println("dgraph-0001: route.indexOf O(P) cycle check in k-shortest-paths"); testCorrectness(); testComplexity(); System.out.println("2/2 PASS"); } }