1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.collections4.bloomfilter;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertNotEquals;
21 import static org.junit.jupiter.api.Assertions.assertThrows;
22
23 import java.util.Arrays;
24
25 import org.junit.jupiter.api.Test;
26 import org.junit.jupiter.params.ParameterizedTest;
27 import org.junit.jupiter.params.provider.CsvSource;
28
29
30
31
32 public class ShapeTest {
33
34
35
36
37
38
39
40
41
42
43
44
45
46 private final Shape shape = Shape.fromKM(3, 24);
47
48
49
50
51 @Test
52 public void testBadNumberOfBits() {
53 assertThrows(IllegalArgumentException.class, () -> Shape.fromKM(5, 0));
54 assertThrows(IllegalArgumentException.class, () -> Shape.fromNM(5, 0));
55 assertThrows(IllegalArgumentException.class, () -> Shape.fromNMK(5, 0, 7));
56 assertThrows(IllegalArgumentException.class, () -> Shape.fromPMK(0.035, 0, 7));
57 }
58
59
60
61
62 @Test
63 public void testBadNumberOfHashFunctions() {
64 assertThrows(IllegalArgumentException.class, () -> Shape.fromKM(0, 7));
65 assertThrows(IllegalArgumentException.class, () -> Shape.fromNMK(5, 26, 0));
66 assertThrows(IllegalArgumentException.class, () -> Shape.fromPMK(0.35, 26, 0));
67 assertThrows(IllegalArgumentException.class, () -> Shape.fromNM(2, 1));
68 }
69
70
71
72
73 @Test
74 public void testBadNumberOfItems() {
75 assertThrows(IllegalArgumentException.class, () -> Shape.fromNM(0, 24));
76 assertThrows(IllegalArgumentException.class, () -> Shape.fromNMK(0, 24, 5));
77 assertThrows(IllegalArgumentException.class, () -> Shape.fromNP(0, 0.02));
78 }
79
80
81
82
83 @Test
84 public void testBadProbability() {
85 assertThrows(IllegalArgumentException.class, () -> Shape.fromNMK(4000, 8, 1));
86 assertThrows(IllegalArgumentException.class, () -> Shape.fromNP(10, 0.0));
87 assertThrows(IllegalArgumentException.class, () -> Shape.fromNP(10, 1.0));
88 assertThrows(IllegalArgumentException.class, () -> Shape.fromNP(10, Double.NaN));
89 assertThrows(IllegalArgumentException.class, () -> Shape.fromNP(10, Double.POSITIVE_INFINITY));
90 assertThrows(IllegalArgumentException.class, () -> Shape.fromNP(10, Double.NEGATIVE_INFINITY));
91 }
92
93
94
95
96 @ParameterizedTest
97 @CsvSource({
98 "3, 24",
99 "1, 24",
100 "1, 1",
101 "13, 124",
102 "13, 224",
103 })
104 public void testEqualsAndHashCode(final int k, final int m) {
105 final Shape shape1 = Shape.fromKM(k, m);
106 assertEquals(shape1, shape1);
107 assertEquals(Arrays.hashCode(new int[] {m, k}), shape1.hashCode(),
108 "Doesn't match Arrays.hashCode(new int[] {m, k})");
109 assertNotEquals(shape1, null);
110 assertNotEquals(shape1, "text");
111 assertNotEquals(shape1, Integer.valueOf(3));
112 assertNotEquals(shape1, Shape.fromKM(k, m + 1));
113 assertNotEquals(shape1, Shape.fromKM(k + 1, m));
114
115
116 final Shape shape2 = Shape.fromKM(k, m);
117 assertEquals(shape1, shape2);
118 assertEquals(shape1.hashCode(), shape2.hashCode());
119 }
120
121
122
123
124
125
126
127
128
129
130
131
132
133 @Test
134 public void testEstimateN() {
135 for (int i = 0; i < 24; i++) {
136 final double c = i;
137 final double expected = -(24.0 / 3.0) * Math.log1p(-c / 24.0);
138 assertEquals(expected, shape.estimateN(i), "Error on " + i);
139 }
140
141 assertEquals(Double.POSITIVE_INFINITY, shape.estimateN(24));
142
143 assertEquals(Double.NaN, shape.estimateN(25));
144 }
145
146
147
148
149 @Test
150 public void testFromKM() {
151 assertThrows(IllegalArgumentException.class, () -> Shape.fromKM(5, 0));
152 assertThrows(IllegalArgumentException.class, () -> Shape.fromKM(0, 5));
153 }
154
155
156
157
158 @Test
159 public void testFromNM() {
160
161
162
163 final Shape shape = Shape.fromNM(5, 24);
164
165 assertEquals(24, shape.getNumberOfBits());
166 assertEquals(3, shape.getNumberOfHashFunctions());
167 assertEquals(0.100375138, shape.getProbability(5), 0.000001);
168
169 assertThrows(IllegalArgumentException.class, () -> Shape.fromNM(5, 0));
170 assertThrows(IllegalArgumentException.class, () -> Shape.fromNM(0, 5));
171 }
172
173
174
175
176
177 @Test
178 public void testFromNMK() {
179
180
181
182 final Shape shape = Shape.fromNMK(5, 24, 4);
183
184 assertEquals(24, shape.getNumberOfBits());
185 assertEquals(4, shape.getNumberOfHashFunctions());
186 assertEquals(0.102194782, shape.getProbability(5), 0.000001);
187
188 assertThrows(IllegalArgumentException.class,
189 () -> Shape.fromNMK(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE));
190 assertThrows(IllegalArgumentException.class, () -> Shape.fromNMK(5, 5, 0));
191 assertThrows(IllegalArgumentException.class, () -> Shape.fromNMK(5, 0, 5));
192 assertThrows(IllegalArgumentException.class, () -> Shape.fromNMK(0, 5, 5));
193 }
194
195
196
197
198
199 @Test
200 public void testFromNP() {
201
202
203
204 final double probability = 1.0 / 2000000;
205 final Shape shape = Shape.fromNP(10, probability);
206
207 assertEquals(302, shape.getNumberOfBits());
208 assertEquals(21, shape.getNumberOfHashFunctions());
209
210 assertThrows(IllegalArgumentException.class, () -> Shape.fromNP(Integer.MAX_VALUE, Math.nextDown(1.0)));
211 assertThrows(IllegalArgumentException.class, () -> Shape.fromNP(0, probability));
212 assertThrows(IllegalArgumentException.class, () -> Shape.fromNP(5, 0.0));
213 assertThrows(IllegalArgumentException.class, () -> Shape.fromNP(Integer.MAX_VALUE, Math.nextUp(0.0)));
214
215
216 assertThrows(IllegalArgumentException.class, () -> Shape.fromNP(Integer.MAX_VALUE, 0.1));
217 }
218
219
220
221
222
223 @Test
224 public void testFromPMK() {
225
226
227
228 Shape shape = Shape.fromPMK(0.1, 24, 3);
229
230 assertEquals(24, shape.getNumberOfBits());
231 assertEquals(3, shape.getNumberOfHashFunctions());
232 assertEquals(0.100375138, shape.getProbability(5), 0.000001);
233
234 assertThrows(IllegalArgumentException.class,
235 () -> Shape.fromPMK(Math.nextDown(1.0), Integer.MAX_VALUE, Integer.MAX_VALUE));
236 shape = Shape.fromPMK(Math.nextUp(0.0), 5, 5);
237 assertEquals(1.0, shape.getProbability(Integer.MAX_VALUE));
238 assertThrows(IllegalArgumentException.class, () -> Shape.fromPMK(Math.nextDown(1.0), 5, 5));
239 assertThrows(IllegalArgumentException.class, () -> Shape.fromPMK(0.0, 5, 5));
240 assertThrows(IllegalArgumentException.class, () -> Shape.fromPMK(0.5, 0, 5));
241 assertThrows(IllegalArgumentException.class, () -> Shape.fromPMK(0.5, 5, 0));
242 }
243
244 @Test
245 public void testGetProbability() {
246 for (int i = 0; i <= 24; i++) {
247 final double expected = Math.pow(-Math.expm1(-3.0 * i / 24), 3);
248 assertEquals(expected, shape.getProbability(i), "error at " + i);
249 }
250
251 assertEquals(0.0, shape.getProbability(0), 0.0);
252
253 assertThrows(IllegalArgumentException.class, () -> shape.getProbability(-1));
254 }
255
256 @Test
257 public void testIsSparse() {
258 final int functions = 1;
259 for (int i = 1; i <= 3; i++) {
260 final int bits = i * Long.SIZE;
261 final Shape shape = Shape.fromKM(functions, bits);
262 for (int n = 0; n <= bits; n++) {
263 final int c = n;
264
265
266 assertEquals(n * Integer.SIZE <= Math.ceil((double) bits / Long.SIZE) * Long.SIZE,
267 shape.isSparse(n), () -> String.format("n=%d : bits=%d", c, bits));
268 }
269 }
270 }
271
272
273
274
275 @Test
276 public void testProbability() {
277 final Shape shape = Shape.fromNMK(5, 24, 3);
278 assertEquals(24, shape.getNumberOfBits());
279 assertEquals(3, shape.getNumberOfHashFunctions());
280 assertEquals(0.100375138, shape.getProbability(5), 0.000001);
281 }
282
283 @Test
284 public void testToString() {
285 assertEquals("Shape[k=3 m=5]", Shape.fromKM(3, 5).toString());
286 }
287 }