diff --git a/src/main/java/org/apache/commons/collections4/bloomfilter/BloomFilter.java b/src/main/java/org/apache/commons/collections4/bloomfilter/BloomFilter.java index acd5032643..f51eb081fc 100644 --- a/src/main/java/org/apache/commons/collections4/bloomfilter/BloomFilter.java +++ b/src/main/java/org/apache/commons/collections4/bloomfilter/BloomFilter.java @@ -305,6 +305,7 @@ default int estimateIntersection(final BloomFilter other) { // maximum estimate value using integer values is: 46144189292 thus // eThis + eOther can not overflow the long value. estimate = Math.round(eThis + eOther - eUnion); + estimate = estimate < 0 ? 0 : estimate; } return estimate>Integer.MAX_VALUE?Integer.MAX_VALUE:(int) estimate; } diff --git a/src/test/java/org/apache/commons/collections4/bloomfilter/AbstractBloomFilterTest.java b/src/test/java/org/apache/commons/collections4/bloomfilter/AbstractBloomFilterTest.java index 4b5a275e57..5e9d91a186 100644 --- a/src/test/java/org/apache/commons/collections4/bloomfilter/AbstractBloomFilterTest.java +++ b/src/test/java/org/apache/commons/collections4/bloomfilter/AbstractBloomFilterTest.java @@ -214,6 +214,17 @@ public void testClear() { assertEquals(0, bf1.cardinality()); } + @Test + public final void testNegativeIntersection() { + IndexProducer p1 = IndexProducer.fromIndexArray(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 20, 26, 28, 30, 32, 34, 35, 36, 37, 39, 40, 41, 42, 43, 45, 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71); + IndexProducer p2 = IndexProducer.fromIndexArray(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27); + + BloomFilter filter1 = createEmptyFilter(Shape.fromKM(17, 72)); + filter1.merge(p1); + BloomFilter filter2 = createEmptyFilter(Shape.fromKM(17, 72)); + filter2.merge(p2); + assertEquals(0, filter1.estimateIntersection(filter2)); + } /** * Tests that the estimated intersection calculations are correct. */ @@ -424,11 +435,11 @@ public void testBitMapProducerSize() { /** * Testing class returns the value as the only value. */ - class BadHasher implements Hasher { + public static class BadHasher implements Hasher { IndexProducer producer; - BadHasher(final int value) { + public BadHasher(final int value) { this.producer = IndexProducer.fromIndexArray(new int[] {value}); } diff --git a/src/test/java/org/apache/commons/collections4/bloomfilter/ArrayHasher.java b/src/test/java/org/apache/commons/collections4/bloomfilter/ArrayHasher.java index 84e223ddbf..f2a18c426a 100644 --- a/src/test/java/org/apache/commons/collections4/bloomfilter/ArrayHasher.java +++ b/src/test/java/org/apache/commons/collections4/bloomfilter/ArrayHasher.java @@ -24,10 +24,10 @@ * *

To be used for testing only.

*/ -final class ArrayHasher implements Hasher { +public final class ArrayHasher implements Hasher { private final int[] values; - ArrayHasher(final int... values) { + public ArrayHasher(final int... values) { this.values = values; } diff --git a/src/test/java/org/apache/commons/collections4/bloomfilter/DefaultIndexProducerTest.java b/src/test/java/org/apache/commons/collections4/bloomfilter/DefaultIndexProducerTest.java index c09158c355..2682a96a7e 100644 --- a/src/test/java/org/apache/commons/collections4/bloomfilter/DefaultIndexProducerTest.java +++ b/src/test/java/org/apache/commons/collections4/bloomfilter/DefaultIndexProducerTest.java @@ -74,7 +74,7 @@ protected int getForEachIndexBehaviour() { * @param bound the upper bound (exclusive) of the values in the array. * @return an array of int. */ - static int[] generateIntArray(final int size, final int bound) { + public static int[] generateIntArray(final int size, final int bound) { return ThreadLocalRandom.current().ints(size, 0, bound).toArray(); } @@ -83,7 +83,7 @@ static int[] generateIntArray(final int size, final int bound) { * @param ary the array * @return the set. */ - static BitSet uniqueSet(final int[] ary) { + public static BitSet uniqueSet(final int[] ary) { final BitSet bs = new BitSet(); Arrays.stream(ary).forEach(bs::set); return bs; @@ -94,7 +94,7 @@ static BitSet uniqueSet(final int[] ary) { * @param ary the array to sort and make unique * @return the sorted unique array. */ - static int[] unique(final int[] ary) { + public static int[] unique(final int[] ary) { return Arrays.stream(ary).distinct().sorted().toArray(); } diff --git a/src/test/java/org/apache/commons/collections4/bloomfilter/IncrementingHasher.java b/src/test/java/org/apache/commons/collections4/bloomfilter/IncrementingHasher.java index ac0fd5111b..a680ac565e 100644 --- a/src/test/java/org/apache/commons/collections4/bloomfilter/IncrementingHasher.java +++ b/src/test/java/org/apache/commons/collections4/bloomfilter/IncrementingHasher.java @@ -25,7 +25,7 @@ * *

To be used for testing only.

*/ -final class IncrementingHasher implements Hasher { +public final class IncrementingHasher implements Hasher { /** * The initial hash value. @@ -46,7 +46,7 @@ final class IncrementingHasher implements Hasher { * @param initial The initial value for the hasher. * @param increment The value to increment the hash by on each iteration. */ - IncrementingHasher(final long initial, final long increment) { + public IncrementingHasher(final long initial, final long increment) { this.initial = initial; this.increment = increment; } diff --git a/src/test/java/org/apache/commons/collections4/bloomfilter/TestingHashers.java b/src/test/java/org/apache/commons/collections4/bloomfilter/TestingHashers.java index 8589000fbb..27a13fd7c4 100644 --- a/src/test/java/org/apache/commons/collections4/bloomfilter/TestingHashers.java +++ b/src/test/java/org/apache/commons/collections4/bloomfilter/TestingHashers.java @@ -18,16 +18,16 @@ /** * A collection of methods and statics that represent standard hashers in testing. */ -class TestingHashers { +public class TestingHashers { /** * Hasher that increments from 1. */ - static final Hasher FROM1 = new IncrementingHasher(1, 1); + public static final Hasher FROM1 = new IncrementingHasher(1, 1); /** * Hasher that increments from 11. */ - static final Hasher FROM11 = new IncrementingHasher(11, 1); + public static final Hasher FROM11 = new IncrementingHasher(11, 1); /** * Do not instantiate. @@ -41,7 +41,7 @@ private TestingHashers() {} * @param hashers The hashers to merge * @return {@code filter} for chaining */ - static T mergeHashers(T filter, Hasher...hashers) { + public static T mergeHashers(T filter, Hasher...hashers) { for (Hasher h : hashers) { filter.merge(h); } @@ -54,7 +54,7 @@ static T mergeHashers(T filter, Hasher...hashers) { * @param filter The Bloom filter to populate * @return {@code filter} for chaining */ - static T populateFromHashersFrom1AndFrom11(T filter) { + public static T populateFromHashersFrom1AndFrom11(T filter) { return mergeHashers(filter, FROM1, FROM11); } @@ -64,7 +64,7 @@ static T populateFromHashersFrom1AndFrom11(T filter) { * @param filter the Bloom filter to populate * @return {@code filter} for chaining */ - static T populateEntireFilter(T filter) { + public static T populateEntireFilter(T filter) { int n = filter.getShape().getNumberOfBits(); int k = filter.getShape().getNumberOfHashFunctions(); for (int i = 0; i < n; i += k) {