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