Author: j16sdiz
Date: 2008-08-29 13:58:11 +0000 (Fri, 29 Aug 2008)
New Revision: 22214
Added:
branches/saltedhashstore/freenet/test/freenet/support/BloomFilterTest.java
Log:
unit test for bloom filter
Added:
branches/saltedhashstore/freenet/test/freenet/support/BloomFilterTest.java
===================================================================
--- branches/saltedhashstore/freenet/test/freenet/support/BloomFilterTest.java
(rev 0)
+++ branches/saltedhashstore/freenet/test/freenet/support/BloomFilterTest.java
2008-08-29 13:58:11 UTC (rev 22214)
@@ -0,0 +1,154 @@
+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;
+
+import org.spaceroots.mantissa.random.MersenneTwister;
+
+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 MersenneTwister();
+
+ 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);
+ }
+}