java-topology/defects/frrouting/unit/FrrCommunityUniqSortAlgorithmTest.java

189 lines
6.6 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 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<Long> 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);
}
}