153 lines
5.8 KiB
Java
153 lines
5.8 KiB
Java
package unit;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* Models BIRD routing daemon community set operations.
|
||
*
|
||
* bird-0003: int_set_union O(N×M) — contains check in union loop
|
||
* bird-0004: clist_filter O(L×S) — contains check in filter loop
|
||
*
|
||
* SLOW: O(N×M) union / O(L×S) filter — linear scan per element
|
||
* FAST: O(N+M) union / O(L+S) filter — HashSet pre-built from second operand
|
||
*
|
||
* CWE-407: nest/a-set.c:394 (union), filter/data.c:421 (filter)
|
||
*/
|
||
public class BirdCommunitySetAlgorithmTest {
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Slow (defective) — mirrors int_set_contains in loop
|
||
// -------------------------------------------------------------------------
|
||
|
||
static class SlowCommunityOps {
|
||
long scanOps = 0;
|
||
|
||
boolean contains(int[] arr, int val) {
|
||
for (int v : arr) {
|
||
scanOps++;
|
||
if (v == val) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/** bird-0003: O(N×M) union */
|
||
int[] union(int[] l1, int[] l2) {
|
||
List<Integer> result = new ArrayList<>();
|
||
for (int v : l1) result.add(v);
|
||
for (int v : l2) {
|
||
if (!contains(l1, v)) result.add(v);
|
||
}
|
||
return result.stream().mapToInt(Integer::intValue).toArray();
|
||
}
|
||
|
||
/** bird-0004: O(L×S) filter — keep entries in l1 that are in filterSet */
|
||
int[] filter(int[] l1, int[] filterSet) {
|
||
List<Integer> result = new ArrayList<>();
|
||
for (int v : l1) {
|
||
if (contains(filterSet, v)) result.add(v);
|
||
}
|
||
return result.stream().mapToInt(Integer::intValue).toArray();
|
||
}
|
||
|
||
long unionOps(int[] l1, int[] l2) { scanOps = 0; union(l1, l2); return scanOps; }
|
||
long filterOps(int[] l1, int[] filterSet) { scanOps = 0; filter(l1, filterSet); return scanOps; }
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// Fast (fixed) — HashSet from one operand
|
||
// -------------------------------------------------------------------------
|
||
|
||
static class FastCommunityOps {
|
||
long scanOps = 0;
|
||
|
||
int[] union(int[] l1, int[] l2) {
|
||
Set<Integer> seen = new HashSet<>();
|
||
for (int v : l1) { seen.add(v); scanOps++; }
|
||
List<Integer> result = new ArrayList<>(Arrays.stream(l1).boxed().toList());
|
||
for (int v : l2) {
|
||
scanOps++;
|
||
if (seen.add(v)) result.add(v);
|
||
}
|
||
return result.stream().mapToInt(Integer::intValue).toArray();
|
||
}
|
||
|
||
int[] filter(int[] l1, int[] filterSet) {
|
||
Set<Integer> fs = new HashSet<>();
|
||
for (int v : filterSet) { fs.add(v); scanOps++; }
|
||
List<Integer> result = new ArrayList<>();
|
||
for (int v : l1) {
|
||
scanOps++;
|
||
if (fs.contains(v)) result.add(v);
|
||
}
|
||
return result.stream().mapToInt(Integer::intValue).toArray();
|
||
}
|
||
|
||
long unionOps(int[] l1, int[] l2) { scanOps = 0; union(l1, l2); return scanOps; }
|
||
long filterOps(int[] l1, int[] filterSet) { scanOps = 0; filter(l1, filterSet); return scanOps; }
|
||
}
|
||
|
||
static int[] range(int from, int n) {
|
||
int[] a = new int[n];
|
||
for (int i = 0; i < n; i++) a[i] = from + i;
|
||
return a;
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
SlowCommunityOps slow = new SlowCommunityOps();
|
||
FastCommunityOps fast = new FastCommunityOps();
|
||
|
||
int passed = 0, total = 0;
|
||
|
||
System.out.println("=== bird-0003: int_set_union O(N×M) ===");
|
||
int[] sizes = {50, 100, 200, 500};
|
||
for (int n : sizes) {
|
||
int[] l1 = range(0, n);
|
||
int[] l2 = range(n / 2, n); // partial overlap
|
||
long s = slow.unionOps(l1, l2);
|
||
long f = fast.unionOps(l1, l2);
|
||
double ratio = (double) s / f;
|
||
total++;
|
||
boolean ok = s > f && ratio >= 5.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++;
|
||
}
|
||
|
||
System.out.println("=== bird-0004: clist_filter O(L×S) ===");
|
||
for (int n : sizes) {
|
||
int[] l1 = range(0, n); // route's community list
|
||
int[] filterSet = range(n/4, n/2); // keep middle half
|
||
long s = slow.filterOps(l1, filterSet);
|
||
long f = fast.filterOps(l1, filterSet);
|
||
double ratio = (double) s / f;
|
||
total++;
|
||
boolean ok = s > f && ratio >= 3.0;
|
||
System.out.printf("L=%3d S=%3d slow=%8d fast=%5d ratio=%6.1fx %s%n",
|
||
n, n/2, s, f, ratio, ok ? "PASS" : "FAIL");
|
||
if (ok) passed++;
|
||
}
|
||
|
||
// Correctness: union contains all unique elements
|
||
int[] a = range(0, 100), b = range(50, 100);
|
||
int[] slowUnion = slow.union(a, b);
|
||
int[] fastUnion = fast.union(a, b);
|
||
Arrays.sort(slowUnion); Arrays.sort(fastUnion);
|
||
total++;
|
||
boolean correct1 = Arrays.equals(slowUnion, fastUnion);
|
||
System.out.printf("union correctness: %s%n", correct1 ? "PASS" : "FAIL");
|
||
if (correct1) passed++;
|
||
|
||
// Correctness: filter returns same kept elements
|
||
int[] l = range(0, 100), fs = range(20, 60);
|
||
int[] slowFilter = slow.filter(l, fs);
|
||
int[] fastFilter = fast.filter(l, fs);
|
||
Arrays.sort(slowFilter); Arrays.sort(fastFilter);
|
||
total++;
|
||
boolean correct2 = Arrays.equals(slowFilter, fastFilter);
|
||
System.out.printf("filter correctness: %s%n", correct2 ? "PASS" : "FAIL");
|
||
if (correct2) passed++;
|
||
|
||
System.out.printf("%n%d/%d PASS%n", passed, total);
|
||
if (passed < total) System.exit(1);
|
||
}
|
||
}
|