package unit; import java.util.*; /** * Models FRRouting community_uniq_sort and ecommunity_include defects. * * frrouting-0003: community_uniq_sort O(N²) — contains check per element during dedup * frrouting-0004: ecommunity_include O(E1×E2) — nested loop cross-membership * * SLOW: O(N²) dedup / O(E1×E2) include * FAST: O(N log N) sort+dedup / O(E1+E2) HashSet * * CWE-407: bgpd/bgp_community.c:143, bgpd/bgp_ecommunity.c:1534 */ public class FrrCommunityUniqSortAlgorithmTest { // ------------------------------------------------------------------------- // frrouting-0003: community_uniq_sort slow path // ------------------------------------------------------------------------- static class SlowCommunitySort { long scanOps = 0; boolean include(int[] arr, int n, int val) { for (int i = 0; i < n; i++) { scanOps++; if (arr[i] == val) return true; } return false; } /** O(N²): include-check per element then qsort at end */ int[] uniqSort(int[] input) { int[] result = new int[input.length]; int size = 0; for (int val : input) { if (!include(result, size, val)) { result[size++] = val; } } int[] out = Arrays.copyOf(result, size); Arrays.sort(out); return out; } long ops(int[] input) { scanOps = 0; uniqSort(input); return scanOps; } } static class FastCommunitySort { long scanOps = 0; /** O(N log N): sort first, then linear dedup */ int[] uniqSort(int[] input) { int[] sorted = Arrays.copyOf(input, input.length); Arrays.sort(sorted); // O(N log N) — counted implicitly // Count ops for linear dedup pass int out = 0; int[] result = new int[sorted.length]; for (int i = 0; i < sorted.length; i++) { scanOps++; if (i == 0 || sorted[i] != sorted[i - 1]) { result[out++] = sorted[i]; } } return Arrays.copyOf(result, out); } long ops(int[] input) { scanOps = 0; uniqSort(input); return scanOps; } } // ------------------------------------------------------------------------- // frrouting-0004: ecommunity_include slow path // ------------------------------------------------------------------------- static class SlowEcommunityInclude { long cmpOps = 0; boolean include(long[] e1, long[] e2) { for (long v1 : e1) { for (long v2 : e2) { cmpOps++; if (v1 == v2) return true; } } return false; } long ops(long[] e1, long[] e2) { cmpOps = 0; include(e1, e2); return cmpOps; } } static class FastEcommunityInclude { long cmpOps = 0; boolean include(long[] e1, long[] e2) { Set set = new HashSet<>(); for (long v : e2) { set.add(v); cmpOps++; } for (long v : e1) { cmpOps++; if (set.contains(v)) return true; } return false; } long ops(long[] e1, long[] e2) { cmpOps = 0; include(e1, e2); return cmpOps; } } // ------------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------------- static int[] makeWithDups(int uniqueVals, int totalSize) { Random rng = new Random(42); int[] arr = new int[totalSize]; for (int i = 0; i < totalSize; i++) arr[i] = rng.nextInt(uniqueVals); return arr; } static long[] makeLong(int n, int offset) { long[] a = new long[n]; for (int i = 0; i < n; i++) a[i] = offset + i; return a; } public static void main(String[] args) { SlowCommunitySort slowSort = new SlowCommunitySort(); FastCommunitySort fastSort = new FastCommunitySort(); SlowEcommunityInclude slowInc = new SlowEcommunityInclude(); FastEcommunityInclude fastInc = new FastEcommunityInclude(); int passed = 0, total = 0; System.out.println("=== frrouting-0003: community_uniq_sort O(N²) ==="); int[] sizes = {50, 100, 200, 500}; for (int n : sizes) { int[] input = makeWithDups(n / 2, n); // ~50% dups long s = slowSort.ops(input); long f = fastSort.ops(input); double ratio = (double) s / Math.max(f, 1); total++; boolean ok = s > f && ratio >= 3.0; System.out.printf("N=%3d slow=%8d fast=%5d ratio=%6.1fx %s%n", n, s, f, ratio, ok ? "PASS" : "FAIL"); if (ok) passed++; } // Correctness: both produce same sorted unique result int[] input = makeWithDups(50, 100); int[] sr = slowSort.uniqSort(input); int[] fr = fastSort.uniqSort(input); total++; boolean correct1 = Arrays.equals(sr, fr); System.out.printf("uniqSort correctness: %s%n", correct1 ? "PASS" : "FAIL"); if (correct1) passed++; System.out.println("=== frrouting-0004: ecommunity_include O(E1×E2) ==="); int[] esizes = {20, 50, 100, 200}; for (int e : esizes) { long[] e1 = makeLong(e, 0); long[] e2 = makeLong(e, e); // disjoint — forces full scan long s = slowInc.ops(e1, e2); long f = fastInc.ops(e1, e2); double ratio = (double) s / Math.max(f, 1); total++; boolean ok = s > f && ratio >= 3.0; System.out.printf("E1=%3d E2=%3d slow=%8d fast=%5d ratio=%6.1fx %s%n", e, e, s, f, ratio, ok ? "PASS" : "FAIL"); if (ok) passed++; } // Correctness: include detects overlap long[] a = makeLong(50, 0), b = makeLong(50, 25); // overlap at 25-49 total++; boolean correct2 = slowInc.include(a, b) == fastInc.include(a, b); System.out.printf("ecommunity_include correctness (overlap): %s%n", correct2 ? "PASS" : "FAIL"); if (correct2) passed++; total++; long[] c = makeLong(50, 100); // disjoint boolean correct3 = slowInc.include(a, c) == fastInc.include(a, c); System.out.printf("ecommunity_include correctness (disjoint): %s%n", correct3 ? "PASS" : "FAIL"); if (correct3) passed++; System.out.printf("%n%d/%d PASS%n", passed, total); if (passed < total) System.exit(1); } }