package freenet.support;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import junit.framework.TestCase;
public class BloomFilterTest extends TestCase {
private static final int FILTER_SIZE = 4 * 1024; // MUST be > PASS,
private static final int PASS = 2048;
private static final int PASS_REMOVE = 4096;
private static final int PASS_POS = 256;
private static final int PASS_FALSE = 8192;
private final Random rand = new Random(12345);
private void _testFilterPositive(BloomFilter filter) {
byte[][] list = new byte[PASS_POS][];
for (int i = 0; i < PASS_POS; i++) {
byte[] b = new byte[32];
rand.nextBytes(b);
filter.addKey(b);
list[i] = b;
}
for (byte[] b : list) {
assertTrue(filter.checkFilter(b));
}
}
public void testCountingFilterPositive() {
int K = BloomFilter.optimialK(FILTER_SIZE, PASS_POS);
BloomFilter filter = BloomFilter.createFilter(FILTER_SIZE, K, true);
_testFilterPositive(filter);
}
public void testBinaryFilterPositive() {
int K = BloomFilter.optimialK(FILTER_SIZE, PASS_POS);
BloomFilter filter = BloomFilter.createFilter(FILTER_SIZE, K, false);
_testFilterPositive(filter);
}
public void testCountingFilterRemove() {
int K = BloomFilter.optimialK(FILTER_SIZE, PASS);
BloomFilter filter = BloomFilter.createFilter(FILTER_SIZE, K, true);
Map<ByteArrayWrapper, byte[]> baseList = new HashMap<ByteArrayWrapper, byte[]>();
// Add Keys
for (int i = 0; i < PASS; i++) {
byte[] b = new byte[32];
do {
rand.nextBytes(b);
} while (baseList.containsKey(new ByteArrayWrapper(b)));
filter.addKey(b);
baseList.put(new ByteArrayWrapper(b), b);
assertTrue("check add BASE", filter.checkFilter(b));
}
// Add some FALSE_PASS keys
Map<ByteArrayWrapper, byte[]> newList = new HashMap<ByteArrayWrapper, byte[]>();
int fPos = 0;
for (int i = 0; i < PASS_REMOVE; i++) {
byte[] b = new byte[64];
ByteArrayWrapper wrapper;
do {
rand.nextBytes(b);
wrapper = new ByteArrayWrapper(b);
} while (newList.containsKey(wrapper));
filter.addKey(b);
newList.put(wrapper, b);
assertTrue("check add NEW", filter.checkFilter(b));
}
// Remove the "NEW" keys and count false positive
for (byte[] b : newList.values())
filter.removeKey(b);
for (byte[] b : newList.values())
if (filter.checkFilter(b))
fPos++;
// Check if some should were removed
assertFalse("100% false positive?", fPos == PASS_REMOVE);
// Check if old keys still here
for (byte[] b : baseList.values())
assertTrue("check original", filter.checkFilter(b));
}
private void _testFilterFalsePositive(BloomFilter filter) {
Set<ByteArrayWrapper> list = new HashSet<ByteArrayWrapper>();
// Add Keys
for (int i = 0; i < PASS; i++) {
byte[] b = new byte[32];
do {
rand.nextBytes(b);
} while (list.contains(new ByteArrayWrapper(b)));
filter.addKey(b);
list.add(new ByteArrayWrapper(b));
assertTrue("check add", filter.checkFilter(b));
}
System.out.println("---" + filter + "---");
int fPos = 0;
for (int i = 0; i < PASS_FALSE; i++) {
byte[] b = new byte[64]; // 64 bytes, sure not exist
rand.nextBytes(b);
if (filter.checkFilter(b))
fPos++;
}
final int K = filter.getK();
final double q = 1 - Math.pow(1 - 1.0 / FILTER_SIZE, K * PASS);
final double p = Math.pow(q, K);
final double actual = (double) fPos / PASS_FALSE;
final double limit = p * 1.05 + 1.0 / PASS_FALSE;
//*-
System.out.println(" k = " + K);
System.out.println(" q = " + q);
System.out.println(" p = " + p);
System.out.println(" limit = " + limit);
System.out.println(" actual = " + actual);
System.out.println(" actual / p = " + actual / p);
/**/
assertFalse("false positive, p=" + p + ", actual=" + actual, actual > limit);
}
public void testCountingFilterFalsePositive() {
int K = BloomFilter.optimialK(FILTER_SIZE, PASS);
BloomFilter filter = BloomFilter.createFilter(FILTER_SIZE, K, true);
_testFilterFalsePositive(filter);
}
public void testBinaryFilterFalsePositive() {
int K = BloomFilter.optimialK(FILTER_SIZE, PASS);
BloomFilter filter = BloomFilter.createFilter(FILTER_SIZE, K, false);
_testFilterFalsePositive(filter);
}
}