View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.util;
23  
24  import org.hipparchus.Field;
25  import org.hipparchus.exception.MathIllegalStateException;
26  import org.hipparchus.fraction.BigFraction;
27  import org.hipparchus.fraction.BigFractionField;
28  import org.hipparchus.fraction.Fraction;
29  import org.hipparchus.fraction.FractionField;
30  import org.junit.jupiter.api.BeforeEach;
31  import org.junit.jupiter.api.Test;
32  
33  import java.util.ConcurrentModificationException;
34  import java.util.HashMap;
35  import java.util.HashSet;
36  import java.util.Map;
37  import java.util.Map.Entry;
38  import java.util.NoSuchElementException;
39  import java.util.Random;
40  import java.util.Set;
41  
42  import static org.junit.jupiter.api.Assertions.assertEquals;
43  import static org.junit.jupiter.api.Assertions.assertFalse;
44  import static org.junit.jupiter.api.Assertions.assertNotEquals;
45  import static org.junit.jupiter.api.Assertions.assertTrue;
46  import static org.junit.jupiter.api.Assertions.fail;
47  
48  
49  class OpenIntToFieldHashMapTest {
50  
51      private final Map<Integer, Fraction> javaMap = new HashMap<>();
52      private final FractionField field = FractionField.getInstance();
53  
54      @BeforeEach
55      void setUp() {
56          javaMap.put(50, new Fraction(100.0));
57          javaMap.put(75, new Fraction(75.0));
58          javaMap.put(25, new Fraction(500.0));
59          javaMap.put(Integer.MAX_VALUE, new Fraction(Integer.MAX_VALUE));
60          javaMap.put(0, new Fraction(-1.0));
61          javaMap.put(1, new Fraction(0.0));
62          javaMap.put(33, new Fraction(-0.1));
63          javaMap.put(23234234, new Fraction(-242343.0));
64          javaMap.put(23321, new Fraction (Integer.MIN_VALUE));
65          javaMap.put(-4444, new Fraction(332.0));
66          javaMap.put(-1, new Fraction(-2323.0));
67          javaMap.put(Integer.MIN_VALUE, new Fraction(44.0));
68  
69          /* Add a few more to cause the table to rehash */
70          javaMap.putAll(generate());
71  
72      }
73  
74      private Map<Integer, Fraction> generate() {
75          Map<Integer, Fraction> map = new HashMap<>();
76          Random r = new Random();
77          double dd;
78          for (int i = 0; i < 2000; ++i) {
79              dd = r.nextDouble();
80              try {
81                  map.put(r.nextInt(), new Fraction(dd));
82              } catch (MathIllegalStateException e) {
83                  throw new IllegalStateException("Invalid :" + dd, e);
84              }
85          }
86          return map;
87      }
88  
89      private OpenIntToFieldHashMap<Fraction> createFromJavaMap(Field<Fraction> field) {
90          OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<>(field);
91          for (Map.Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
92              map.put(mapEntry.getKey(), mapEntry.getValue());
93          }
94          return map;
95      }
96  
97      @Test
98      void testPutAndGetWith0ExpectedSize() {
99          OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<>(field,0);
100         customAssertPutAndGet(map);
101     }
102 
103     @Test
104     void testPutAndGetWithExpectedSize() {
105         OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<>(field,500);
106         customAssertPutAndGet(map);
107     }
108 
109     @Test
110     void testPutAndGet() {
111         OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<>(field);
112         customAssertPutAndGet(map);
113     }
114 
115     private void customAssertPutAndGet(OpenIntToFieldHashMap<Fraction> map) {
116         customAssertPutAndGet(map, 0, new HashSet<>());
117     }
118 
119     private void customAssertPutAndGet(OpenIntToFieldHashMap<Fraction> map, int mapSize,
120                                        Set<Integer> keysInMap) {
121         assertEquals(mapSize, map.size());
122         for (Map.Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
123             map.put(mapEntry.getKey(), mapEntry.getValue());
124             if (!keysInMap.contains(mapEntry.getKey()))
125                 ++mapSize;
126             assertEquals(mapSize, map.size());
127             assertEquals(mapEntry.getValue(), map.get(mapEntry.getKey()));
128         }
129     }
130 
131     @Test
132     void testPutAbsentOnExisting() {
133         OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
134         int size = javaMap.size();
135         for (Map.Entry<Integer, Fraction> mapEntry : generateAbsent().entrySet()) {
136             map.put(mapEntry.getKey(), mapEntry.getValue());
137             assertEquals(++size, map.size());
138             assertEquals(mapEntry.getValue(), map.get(mapEntry.getKey()));
139         }
140     }
141 
142     @Test
143     void testPutOnExisting() {
144         OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
145         for (Map.Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
146             map.put(mapEntry.getKey(), mapEntry.getValue());
147             assertEquals(javaMap.size(), map.size());
148             assertEquals(mapEntry.getValue(), map.get(mapEntry.getKey()));
149         }
150     }
151 
152     @Test
153     void testGetAbsent() {
154         Map<Integer, Fraction> generated = generateAbsent();
155         OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
156 
157         for (Map.Entry<Integer, Fraction> mapEntry : generated.entrySet())
158             assertEquals(field.getZero(), map.get(mapEntry.getKey()));
159     }
160 
161     @Test
162     void testGetFromEmpty() {
163         OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<>(field);
164         assertEquals(field.getZero(), map.get(5));
165         assertEquals(field.getZero(), map.get(0));
166         assertEquals(field.getZero(), map.get(50));
167     }
168 
169     @Test
170     void testRemove() {
171         OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
172         int mapSize = javaMap.size();
173         assertEquals(mapSize, map.size());
174         for (Map.Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
175             map.remove(mapEntry.getKey());
176             assertEquals(--mapSize, map.size());
177             assertEquals(field.getZero(), map.get(mapEntry.getKey()));
178         }
179 
180         /* Ensure that put and get still work correctly after removals */
181         customAssertPutAndGet(map);
182     }
183 
184     /* This time only remove some entries */
185     @Test
186     void testRemove2() {
187         OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
188         int mapSize = javaMap.size();
189         int count = 0;
190         Set<Integer> keysInMap = new HashSet<>(javaMap.keySet());
191         for (Map.Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
192             keysInMap.remove(mapEntry.getKey());
193             map.remove(mapEntry.getKey());
194             assertEquals(--mapSize, map.size());
195             assertEquals(field.getZero(), map.get(mapEntry.getKey()));
196             if (count++ > 5)
197                 break;
198         }
199 
200         /* Ensure that put and get still work correctly after removals */
201         customAssertPutAndGet(map, mapSize, keysInMap);
202     }
203 
204     @Test
205     void testRemoveFromEmpty() {
206         OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<>(field);
207         assertEquals(field.getZero(), map.remove(50));
208     }
209 
210     @Test
211     void testRemoveAbsent() {
212         Map<Integer, Fraction> generated = generateAbsent();
213 
214         OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
215         int mapSize = map.size();
216 
217         for (Map.Entry<Integer, Fraction> mapEntry : generated.entrySet()) {
218             map.remove(mapEntry.getKey());
219             assertEquals(mapSize, map.size());
220             assertEquals(field.getZero(), map.get(mapEntry.getKey()));
221         }
222     }
223 
224     /**
225      * Returns a map with at least 100 elements where each element is absent from javaMap.
226      */
227     private Map<Integer, Fraction> generateAbsent() {
228         Map<Integer, Fraction> generated = new HashMap<>();
229         do {
230             generated.putAll(generate());
231             for (Integer key : javaMap.keySet())
232                 generated.remove(key);
233         } while (generated.size() < 100);
234         return generated;
235     }
236 
237     @Test
238     void testCopy() {
239         OpenIntToFieldHashMap<Fraction> copy =
240             new OpenIntToFieldHashMap<>(createFromJavaMap(field));
241         assertEquals(javaMap.size(), copy.size());
242 
243         for (Map.Entry<Integer, Fraction> mapEntry : javaMap.entrySet())
244             assertEquals(mapEntry.getValue(), copy.get(mapEntry.getKey()));
245     }
246 
247     @Test
248     void testContainsKey() {
249         OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
250         for (Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
251             assertTrue(map.containsKey(mapEntry.getKey()));
252         }
253         for (Map.Entry<Integer, Fraction> mapEntry : generateAbsent().entrySet()) {
254             assertFalse(map.containsKey(mapEntry.getKey()));
255         }
256         for (Entry<Integer, Fraction> mapEntry : javaMap.entrySet()) {
257             int key = mapEntry.getKey();
258             assertTrue(map.containsKey(key));
259             map.remove(key);
260             assertFalse(map.containsKey(key));
261         }
262     }
263 
264     @Test
265     void testIterator() {
266         OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
267         OpenIntToFieldHashMap<Fraction>.Iterator iterator = map.iterator();
268         for (int i = 0; i < map.size(); ++i) {
269             assertTrue(iterator.hasNext());
270             iterator.advance();
271             int key = iterator.key();
272             assertTrue(map.containsKey(key));
273             assertEquals(javaMap.get(key), map.get(key));
274             assertEquals(javaMap.get(key), iterator.value());
275             assertTrue(javaMap.containsKey(key));
276         }
277         assertFalse(iterator.hasNext());
278         try {
279             iterator.advance();
280             fail("an exception should have been thrown");
281         } catch (NoSuchElementException nsee) {
282             // expected
283         }
284     }
285 
286     @Test
287     void testEquals() {
288         OpenIntToFieldHashMap<Fraction> map1 = new OpenIntToFieldHashMap<>(FractionField.getInstance());
289         map1.put(2,   new Fraction( 5, 2));
290         map1.put(17,  new Fraction(-1, 2));
291         map1.put(16,  Fraction.ZERO);
292         assertEquals(map1, map1);
293         OpenIntToFieldHashMap<Fraction> map2 = new OpenIntToFieldHashMap<>(FractionField.getInstance());
294         map2.put(17, new Fraction(-1, 2));
295         map2.put(2,  new Fraction( 5, 2));
296         map2.put(16, new Fraction(0));
297         assertEquals(map1, map2);
298         map2.put(16,  new Fraction( 1, 4));
299         assertNotEquals(map1, map2);
300         map2.put(16,  Fraction.ZERO);
301         assertEquals(map1, map2);
302         OpenIntToFieldHashMap<BigFraction> map3 = new OpenIntToFieldHashMap<>(BigFractionField.getInstance());
303         map3.put(2,   new BigFraction( 5, 2));
304         map3.put(17,  new BigFraction(-1, 2));
305         map3.put(16,  BigFraction.ZERO);
306         assertNotEquals(map1, map3);
307         assertNotEquals("", map1);
308         assertNotEquals(null, map1);
309     }
310 
311     @Test
312     void testHashcode() {
313         OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<>(FractionField.getInstance());
314         map.put(2,   new Fraction( 5, 2));
315         map.put(17,  new Fraction(-1, 2));
316         map.put(16,  Fraction.ZERO);
317         assertEquals(-528348218, map.hashCode());
318     }
319 
320     @Test
321     void testConcurrentModification() {
322         OpenIntToFieldHashMap<Fraction> map = createFromJavaMap(field);
323         OpenIntToFieldHashMap<Fraction>.Iterator iterator = map.iterator();
324         map.put(3, new Fraction(3));
325         try {
326             iterator.advance();
327             fail("an exception should have been thrown");
328         } catch (ConcurrentModificationException cme) {
329             // expected
330         }
331     }
332 
333     /**
334      * Regression test for a bug in findInsertionIndex where the hashing in the second probing
335      * loop was inconsistent with the first causing duplicate keys after the right sequence
336      * of puts and removes.
337      */
338     @Test
339     void testPutKeysWithCollisions() {
340         OpenIntToFieldHashMap<Fraction> map = new OpenIntToFieldHashMap<>(field);
341         int key1 = -1996012590;
342         Fraction value1 = new Fraction(1);
343         map.put(key1, value1);
344         int key2 = 835099822;
345         map.put(key2, value1);
346         int key3 = 1008859686;
347         map.put(key3, value1);
348         assertEquals(value1, map.get(key3));
349         assertEquals(3, map.size());
350 
351         map.remove(key2);
352         Fraction value2 = new Fraction(2);
353         map.put(key3, value2);
354         assertEquals(value2, map.get(key3));
355         assertEquals(2, map.size());
356     }
357 
358     /**
359      * Similar to testPutKeysWithCollisions() but exercises the codepaths in a slightly
360      * different manner.
361      */
362     @Test
363     void testPutKeysWithCollision2() {
364         OpenIntToFieldHashMap<Fraction>map = new OpenIntToFieldHashMap<>(field);
365         int key1 = 837989881;
366         Fraction value1 = new Fraction(1);
367         map.put(key1, value1);
368         int key2 = 476463321;
369         map.put(key2, value1);
370         assertEquals(2, map.size());
371         assertEquals(value1, map.get(key2));
372 
373         map.remove(key1);
374         Fraction value2 = new Fraction(2);
375         map.put(key2, value2);
376         assertEquals(1, map.size());
377         assertEquals(value2, map.get(key2));
378     }
379 
380 
381 }