Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2008 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.collect;
     18 
     19 import static com.google.common.truth.Truth.assertThat;
     20 
     21 import com.google.common.collect.testing.IteratorFeature;
     22 import com.google.common.collect.testing.IteratorTester;
     23 import com.google.common.testing.NullPointerTester;
     24 
     25 import junit.framework.TestCase;
     26 
     27 import java.util.ArrayList;
     28 import java.util.Arrays;
     29 import java.util.Collection;
     30 import java.util.Collections;
     31 import java.util.ConcurrentModificationException;
     32 import java.util.Iterator;
     33 import java.util.List;
     34 import java.util.Map;
     35 import java.util.NoSuchElementException;
     36 import java.util.PriorityQueue;
     37 import java.util.Random;
     38 import java.util.SortedMap;
     39 import java.util.concurrent.atomic.AtomicInteger;
     40 
     41 /**
     42  * Unit test for {@link MinMaxPriorityQueue}.
     43  *
     44  * @author Alexei Stolboushkin
     45  * @author Sverre Sundsdal
     46  */
     47 public class MinMaxPriorityQueueTest extends TestCase {
     48   private Ordering<Integer> SOME_COMPARATOR = Ordering.natural().reverse();
     49 
     50   // Overkill alert!  Test all combinations of 0-2 options during creation.
     51 
     52   public void testCreation_simple() {
     53     MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue
     54         .create();
     55     assertEquals(11, queue.capacity());
     56     checkUnbounded(queue);
     57     checkNatural(queue);
     58   }
     59 
     60   public void testCreation_comparator() {
     61     MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue
     62         .orderedBy(SOME_COMPARATOR)
     63         .create();
     64     assertEquals(11, queue.capacity());
     65     checkUnbounded(queue);
     66     assertSame(SOME_COMPARATOR, queue.comparator());
     67   }
     68 
     69   public void testCreation_expectedSize() {
     70     MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue
     71         .expectedSize(8)
     72         .create();
     73     assertEquals(8, queue.capacity());
     74     checkUnbounded(queue);
     75     checkNatural(queue);
     76   }
     77 
     78   public void testCreation_expectedSize_comparator() {
     79     MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue
     80         .orderedBy(SOME_COMPARATOR)
     81         .expectedSize(8)
     82         .create();
     83     assertEquals(8, queue.capacity());
     84     checkUnbounded(queue);
     85     assertSame(SOME_COMPARATOR, queue.comparator());
     86   }
     87 
     88   public void testCreation_maximumSize() {
     89     MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue
     90         .maximumSize(42)
     91         .create();
     92     assertEquals(11, queue.capacity());
     93     assertEquals(42, queue.maximumSize);
     94     checkNatural(queue);
     95   }
     96 
     97   public void testCreation_comparator_maximumSize() {
     98     MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue
     99         .orderedBy(SOME_COMPARATOR)
    100         .maximumSize(42)
    101         .create();
    102     assertEquals(11, queue.capacity());
    103     assertEquals(42, queue.maximumSize);
    104     assertSame(SOME_COMPARATOR, queue.comparator());
    105   }
    106 
    107   public void testCreation_expectedSize_maximumSize() {
    108     MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue
    109         .expectedSize(8)
    110         .maximumSize(42)
    111         .create();
    112     assertEquals(8, queue.capacity());
    113     assertEquals(42, queue.maximumSize);
    114     checkNatural(queue);
    115   }
    116 
    117   private static final List<Integer> NUMBERS =
    118       ImmutableList.of(4, 8, 15, 16, 23, 42);
    119 
    120   public void testCreation_withContents() {
    121     MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue
    122         .create(NUMBERS);
    123     assertEquals(6, queue.size());
    124     assertEquals(11, queue.capacity());
    125     checkUnbounded(queue);
    126     checkNatural(queue);
    127   }
    128 
    129   public void testCreation_comparator_withContents() {
    130     MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue
    131         .orderedBy(SOME_COMPARATOR)
    132         .create(NUMBERS);
    133     assertEquals(6, queue.size());
    134     assertEquals(11, queue.capacity());
    135     checkUnbounded(queue);
    136     assertSame(SOME_COMPARATOR, queue.comparator());
    137   }
    138 
    139   public void testCreation_expectedSize_withContents() {
    140     MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue
    141         .expectedSize(8)
    142         .create(NUMBERS);
    143     assertEquals(6, queue.size());
    144     assertEquals(8, queue.capacity());
    145     checkUnbounded(queue);
    146     checkNatural(queue);
    147   }
    148 
    149   public void testCreation_maximumSize_withContents() {
    150     MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue
    151         .maximumSize(42)
    152         .create(NUMBERS);
    153     assertEquals(6, queue.size());
    154     assertEquals(11, queue.capacity());
    155     assertEquals(42, queue.maximumSize);
    156     checkNatural(queue);
    157   }
    158 
    159   // Now test everything at once
    160 
    161   public void testCreation_allOptions() {
    162     MinMaxPriorityQueue<Integer> queue = MinMaxPriorityQueue
    163         .orderedBy(SOME_COMPARATOR)
    164         .expectedSize(8)
    165         .maximumSize(42)
    166         .create(NUMBERS);
    167     assertEquals(6, queue.size());
    168     assertEquals(8, queue.capacity());
    169     assertEquals(42, queue.maximumSize);
    170     assertSame(SOME_COMPARATOR, queue.comparator());
    171   }
    172 
    173   // TODO: tests that check the weird interplay between expected size,
    174   // maximum size, size of initial contents, default capacity...
    175 
    176   private static void checkNatural(MinMaxPriorityQueue<Integer> queue) {
    177     assertSame(Ordering.natural(), queue.comparator());
    178   }
    179 
    180   private static void checkUnbounded(MinMaxPriorityQueue<Integer> queue) {
    181     assertEquals(Integer.MAX_VALUE, queue.maximumSize);
    182   }
    183 
    184   public void testHeapIntact() {
    185     Random random = new Random(0);
    186     int heapSize = 999;
    187     int numberOfModifications = 500;
    188     MinMaxPriorityQueue<Integer> mmHeap =
    189         MinMaxPriorityQueue.expectedSize(heapSize).create();
    190     /*
    191      * this map would contain the same exact elements as the MinMaxHeap; the
    192      * value in the map is the number of occurrences of the key.
    193      */
    194     SortedMap<Integer, AtomicInteger> replica = Maps.newTreeMap();
    195     assertTrue("Empty heap should be OK", mmHeap.isIntact());
    196     for (int i = 0; i < heapSize; i++) {
    197       int randomInt = random.nextInt();
    198       mmHeap.offer(randomInt);
    199       insertIntoReplica(replica, randomInt);
    200     }
    201     assertTrue("MinMaxHeap not intact after " + heapSize + " insertions",
    202         mmHeap.isIntact());
    203     assertEquals(heapSize, mmHeap.size());
    204     int currentHeapSize = heapSize;
    205     for (int i = 0; i < numberOfModifications; i++) {
    206       if (random.nextBoolean()) {
    207         /* insert a new element */
    208         int randomInt = random.nextInt();
    209         mmHeap.offer(randomInt);
    210         insertIntoReplica(replica, randomInt);
    211         currentHeapSize++;
    212       } else {
    213         /* remove either min or max */
    214         if (random.nextBoolean()) {
    215           removeMinFromReplica(replica, mmHeap.poll());
    216         } else {
    217           removeMaxFromReplica(replica, mmHeap.pollLast());
    218         }
    219         for (Integer v : replica.keySet()) {
    220           assertTrue("MinMax queue has lost " + v, mmHeap.contains(v));
    221         }
    222         assertTrue(mmHeap.isIntact());
    223         currentHeapSize--;
    224         assertEquals(currentHeapSize, mmHeap.size());
    225       }
    226     }
    227     assertEquals(currentHeapSize, mmHeap.size());
    228     assertTrue("Heap not intact after " + numberOfModifications +
    229         " random mixture of operations", mmHeap.isIntact());
    230   }
    231 
    232   public void testSmall() {
    233     MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create();
    234     mmHeap.add(1);
    235     mmHeap.add(4);
    236     mmHeap.add(2);
    237     mmHeap.add(3);
    238     assertEquals(4, (int) mmHeap.pollLast());
    239     assertEquals(3, (int) mmHeap.peekLast());
    240     assertEquals(3, (int) mmHeap.pollLast());
    241     assertEquals(1, (int) mmHeap.peek());
    242     assertEquals(2, (int) mmHeap.peekLast());
    243     assertEquals(2, (int) mmHeap.pollLast());
    244     assertEquals(1, (int) mmHeap.peek());
    245     assertEquals(1, (int) mmHeap.peekLast());
    246     assertEquals(1, (int) mmHeap.pollLast());
    247     assertNull(mmHeap.peek());
    248     assertNull(mmHeap.peekLast());
    249     assertNull(mmHeap.pollLast());
    250   }
    251 
    252   public void testSmallMinHeap() {
    253     MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create();
    254     mmHeap.add(1);
    255     mmHeap.add(3);
    256     mmHeap.add(2);
    257     assertEquals(1, (int) mmHeap.peek());
    258     assertEquals(1, (int) mmHeap.poll());
    259     assertEquals(3, (int) mmHeap.peekLast());
    260     assertEquals(2, (int) mmHeap.peek());
    261     assertEquals(2, (int) mmHeap.poll());
    262     assertEquals(3, (int) mmHeap.peekLast());
    263     assertEquals(3, (int) mmHeap.peek());
    264     assertEquals(3, (int) mmHeap.poll());
    265     assertNull(mmHeap.peekLast());
    266     assertNull(mmHeap.peek());
    267     assertNull(mmHeap.poll());
    268   }
    269 
    270   public void testRemove() {
    271     MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create();
    272     mmHeap.addAll(Lists.newArrayList(1, 2, 3, 4, 47, 1, 5, 3, 0));
    273     assertTrue("Heap is not intact initally", mmHeap.isIntact());
    274     assertEquals(9, mmHeap.size());
    275     mmHeap.remove(5);
    276     assertEquals(8, mmHeap.size());
    277     assertTrue("Heap is not intact after remove()", mmHeap.isIntact());
    278     assertEquals(47, (int) mmHeap.pollLast());
    279     assertEquals(4, (int) mmHeap.pollLast());
    280     mmHeap.removeAll(Lists.newArrayList(2, 3));
    281     assertEquals(3, mmHeap.size());
    282     assertTrue("Heap is not intact after removeAll()", mmHeap.isIntact());
    283   }
    284 
    285   public void testContains() {
    286     MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create();
    287     mmHeap.addAll(Lists.newArrayList(1, 1, 2));
    288     assertEquals(3, mmHeap.size());
    289     assertFalse("Heap does not contain null", mmHeap.contains(null));
    290     assertFalse("Heap does not contain 3", mmHeap.contains(3));
    291     assertFalse("Heap does not contain 3", mmHeap.remove(3));
    292     assertEquals(3, mmHeap.size());
    293     assertTrue("Heap is not intact after remove()", mmHeap.isIntact());
    294     assertTrue("Heap contains two 1's", mmHeap.contains(1));
    295     assertTrue("Heap contains two 1's", mmHeap.remove(1));
    296     assertTrue("Heap contains 1", mmHeap.contains(1));
    297     assertTrue("Heap contains 1", mmHeap.remove(1));
    298     assertFalse("Heap does not contain 1", mmHeap.contains(1));
    299     assertTrue("Heap contains 2", mmHeap.remove(2));
    300     assertEquals(0, mmHeap.size());
    301     assertFalse("Heap does not contain anything", mmHeap.contains(1));
    302     assertFalse("Heap does not contain anything", mmHeap.remove(2));
    303   }
    304 
    305   public void testIteratorPastEndException() {
    306     MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create();
    307     mmHeap.addAll(Lists.newArrayList(1, 2));
    308     Iterator<Integer> it = mmHeap.iterator();
    309     assertTrue("Iterator has reached end prematurely", it.hasNext());
    310     it.next();
    311     it.next();
    312     try {
    313       it.next();
    314       fail("No exception thrown when iterating past end of heap");
    315     } catch (NoSuchElementException e) {
    316       // expected error
    317     }
    318   }
    319 
    320   public void testIteratorConcurrentModification() {
    321     MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create();
    322     mmHeap.addAll(Lists.newArrayList(1, 2, 3, 4));
    323     Iterator<Integer> it = mmHeap.iterator();
    324     assertTrue("Iterator has reached end prematurely", it.hasNext());
    325     it.next();
    326     it.next();
    327     mmHeap.remove(4);
    328     try {
    329       it.next();
    330       fail("No exception thrown when iterating a modified heap");
    331     } catch (ConcurrentModificationException e) {
    332       // expected error
    333     }
    334   }
    335 
    336   /**
    337    * Tests a failure caused by fix to childless uncle issue.
    338    */
    339   public void testIteratorRegressionChildlessUncle() {
    340     final ArrayList<Integer> initial = Lists.newArrayList(
    341         1, 15, 13, 8, 9, 10, 11, 14);
    342     MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(initial);
    343     assertTrue("State " + Arrays.toString(q.toArray()), q.isIntact());
    344     q.remove(9);
    345     q.remove(11);
    346     q.remove(10);
    347     // Now we're in the critical state: [1, 15, 13, 8, 14]
    348     // Removing 8 while iterating caused duplicates in iteration result.
    349     List<Integer> result = Lists.newArrayListWithCapacity(initial.size());
    350     for (Iterator<Integer> iter = q.iterator(); iter.hasNext();) {
    351       Integer value = iter.next();
    352       result.add(value);
    353       if (value == 8) {
    354         iter.remove();
    355       }
    356     }
    357     assertTrue(q.isIntact());
    358     assertThat(result).has().exactly(1, 15, 13, 8, 14);
    359   }
    360 
    361   /**
    362    * This tests a special case of the removeAt() call. Moving an element
    363    * sideways on the heap could break the invariants. Sometimes we need to
    364    * bubble an element up instead of trickling down. See implementation.
    365    */
    366   public void testInvalidatingRemove() {
    367     MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create();
    368     mmHeap.addAll(Lists.newArrayList(
    369             1, 20, 1000, 2, 3, 30, 40, 10, 11, 12, 13, 300, 400, 500, 600));
    370     assertEquals(15, mmHeap.size());
    371     assertTrue("Heap is not intact initially", mmHeap.isIntact());
    372     mmHeap.remove(12);
    373     assertEquals(14, mmHeap.size());
    374     assertTrue("Heap is not intact after remove()", mmHeap.isIntact());
    375   }
    376 
    377   /**
    378    * This tests a more obscure special case, but otherwise similar to above.
    379    */
    380   public void testInvalidatingRemove2() {
    381     MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create();
    382     List<Integer> values = Lists.newArrayList(
    383         1, 20, 1000, 2, 3, 30, 40, 10, 11, 12, 13, 300, 400, 500, 600, 4, 5,
    384         6, 7, 8, 9, 4, 5, 200, 250);
    385     mmHeap.addAll(values);
    386     assertEquals(25, mmHeap.size());
    387     assertTrue("Heap is not intact initially", mmHeap.isIntact());
    388     mmHeap.remove(2);
    389     assertEquals(24, mmHeap.size());
    390     assertTrue("Heap is not intact after remove()", mmHeap.isIntact());
    391     values.removeAll(Lists.newArrayList(2));
    392     assertEquals(values.size(), mmHeap.size());
    393     assertTrue(values.containsAll(mmHeap));
    394     assertTrue(mmHeap.containsAll(values));
    395   }
    396 
    397   public void testIteratorInvalidatingIteratorRemove() {
    398     MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create();
    399     mmHeap.addAll(Lists.newArrayList(
    400             1, 20, 100, 2, 3, 30, 40));
    401     assertEquals(7, mmHeap.size());
    402     assertTrue("Heap is not intact initially", mmHeap.isIntact());
    403     Iterator<Integer> it = mmHeap.iterator();
    404     assertEquals((Integer) 1, it.next());
    405     assertEquals((Integer) 20, it.next());
    406     assertEquals((Integer) 100, it.next());
    407     assertEquals((Integer) 2, it.next());
    408     it.remove();
    409     assertFalse(mmHeap.contains(2));
    410     assertTrue(it.hasNext());
    411     assertEquals((Integer) 3, it.next());
    412     assertTrue(it.hasNext());
    413     assertEquals((Integer) 30, it.next());
    414     assertTrue(it.hasNext());
    415     assertEquals((Integer) 40, it.next());
    416     assertFalse(it.hasNext());
    417     assertEquals(6, mmHeap.size());
    418     assertTrue("Heap is not intact after remove()", mmHeap.isIntact());
    419     assertFalse(mmHeap.contains(2));
    420 
    421     // This tests that it.remove() above actually changed the order. It
    422     // indicates that the value 40 was stored in forgetMeNot, so it is
    423     // returned in the last call to it.next(). Without it, 30 should be the last
    424     // item returned by the iterator.
    425     Integer lastItem = 0;
    426     for (Integer tmp : mmHeap) {
    427       lastItem = tmp;
    428     }
    429     assertEquals((Integer) 30, lastItem);
    430   }
    431 
    432   /**
    433    * This tests a special case where removeAt has to trickle an element
    434    * first down one level from a min to a max level, then up one level above
    435    * the index of the removed element.
    436    * It also tests that skipMe in the iterator plays nicely with
    437    * forgetMeNot.
    438    */
    439   public void testIteratorInvalidatingIteratorRemove2() {
    440     MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.create();
    441     mmHeap.addAll(Lists.newArrayList(
    442         1, 20, 1000, 2, 3, 30, 40, 10, 11, 12, 13, 200, 300, 500, 400));
    443     assertTrue("Heap is not intact initially", mmHeap.isIntact());
    444     Iterator<Integer> it = mmHeap.iterator();
    445     assertEquals((Integer) 1, it.next());
    446     assertEquals((Integer) 20, it.next());
    447     assertEquals((Integer) 1000, it.next());
    448     assertEquals((Integer) 2, it.next());
    449     it.remove();
    450     assertTrue("Heap is not intact after remove", mmHeap.isIntact());
    451     assertEquals((Integer) 10, it.next());
    452     assertEquals((Integer) 3, it.next());
    453     it.remove();
    454     assertTrue("Heap is not intact after remove", mmHeap.isIntact());
    455     assertEquals((Integer) 12, it.next());
    456     assertEquals((Integer) 30, it.next());
    457     assertEquals((Integer) 40, it.next());
    458     // Skipping 20
    459     assertEquals((Integer) 11, it.next());
    460     // Skipping 400
    461     assertEquals((Integer) 13, it.next());
    462     assertEquals((Integer) 200, it.next());
    463     assertEquals((Integer) 300, it.next());
    464     // Last two from forgetMeNot.
    465     assertEquals((Integer) 400, it.next());
    466     assertEquals((Integer) 500, it.next());
    467   }
    468 
    469   public void testRemoveFromStringHeap() {
    470     MinMaxPriorityQueue<String> mmHeap =
    471         MinMaxPriorityQueue.expectedSize(5).create();
    472     Collections.addAll(mmHeap,
    473         "foo", "bar", "foobar", "barfoo", "larry", "sergey", "eric");
    474     assertTrue("Heap is not intact initially", mmHeap.isIntact());
    475     assertEquals("bar", mmHeap.peek());
    476     assertEquals("sergey", mmHeap.peekLast());
    477     assertEquals(7, mmHeap.size());
    478     assertTrue("Could not remove larry", mmHeap.remove("larry"));
    479     assertEquals(6, mmHeap.size());
    480     assertFalse("heap contains larry which has been removed",
    481         mmHeap.contains("larry"));
    482     assertTrue("heap does not contain sergey",
    483         mmHeap.contains("sergey"));
    484     assertTrue("Could not remove larry", mmHeap.removeAll(
    485         Lists.newArrayList("sergey", "eric")));
    486     assertFalse("Could remove nikesh which is not in the heap",
    487         mmHeap.remove("nikesh"));
    488     assertEquals(4, mmHeap.size());
    489   }
    490 
    491   public void testCreateWithOrdering() {
    492     MinMaxPriorityQueue<String> mmHeap =
    493         MinMaxPriorityQueue.orderedBy(Ordering.natural().reverse()).create();
    494     Collections.addAll(mmHeap,
    495         "foo", "bar", "foobar", "barfoo", "larry", "sergey", "eric");
    496     assertTrue("Heap is not intact initially", mmHeap.isIntact());
    497     assertEquals("sergey", mmHeap.peek());
    498     assertEquals("bar", mmHeap.peekLast());
    499   }
    500 
    501   public void testCreateWithCapacityAndOrdering() {
    502     MinMaxPriorityQueue<Integer> mmHeap = MinMaxPriorityQueue.orderedBy(
    503         Ordering.natural().reverse()).expectedSize(5).create();
    504     Collections.addAll(mmHeap, 1, 7, 2, 56, 2, 5, 23, 68, 0, 3);
    505     assertTrue("Heap is not intact initially", mmHeap.isIntact());
    506     assertEquals(68, (int) mmHeap.peek());
    507     assertEquals(0, (int) mmHeap.peekLast());
    508   }
    509 
    510   private <T extends Comparable<T>> void runIterator(
    511       final List<T> values, int steps) throws Exception {
    512     IteratorTester<T> tester =
    513         new IteratorTester<T>(
    514             steps,
    515             IteratorFeature.MODIFIABLE,
    516             Lists.newLinkedList(values),
    517             IteratorTester.KnownOrder.UNKNOWN_ORDER) {
    518           private MinMaxPriorityQueue<T> mmHeap;
    519           @Override protected Iterator<T> newTargetIterator() {
    520             mmHeap = MinMaxPriorityQueue.create(values);
    521             return mmHeap.iterator();
    522           }
    523           @Override protected void verify(List<T> elements) {
    524             assertEquals(Sets.newHashSet(elements),
    525                 Sets.newHashSet(mmHeap.iterator()));
    526             assertTrue("Invalid MinMaxHeap: " + mmHeap, mmHeap.isIntact());
    527           }
    528         };
    529     tester.test();
    530   }
    531 
    532   public void testIteratorTester() throws Exception {
    533     Random random = new Random(0);
    534     List<Integer> list = Lists.newArrayList();
    535     for (int i = 0; i < 3; i++) {
    536       list.add(random.nextInt());
    537     }
    538     runIterator(list, 6);
    539   }
    540 
    541   public void testIteratorTesterLarger() throws Exception {
    542     runIterator(Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 5);
    543   }
    544 
    545   public void testRemoveAt() {
    546     long seed = new Random().nextLong();
    547     Random random = new Random(seed);
    548     int heapSize = 999;
    549     int numberOfModifications = 500;
    550     MinMaxPriorityQueue<Integer> mmHeap =
    551         MinMaxPriorityQueue.expectedSize(heapSize).create();
    552     for (int i = 0; i < heapSize; i++) {
    553       mmHeap.add(random.nextInt());
    554     }
    555     for (int i = 0; i < numberOfModifications; i++) {
    556       mmHeap.removeAt(random.nextInt(mmHeap.size()));
    557       assertTrue("Modification " + i + " of seed " + seed, mmHeap.isIntact());
    558       mmHeap.add(random.nextInt());
    559       assertTrue("Modification " + i + " of seed " + seed, mmHeap.isIntact());
    560     }
    561   }
    562 
    563   public void testRemoveAt_exhaustive() {
    564     int size = 8;
    565     List<Integer> expected = createOrderedList(size);
    566     for (Collection<Integer> perm : Collections2.permutations(expected)) {
    567       for (int i = 0; i < perm.size(); i++) {
    568         MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(perm);
    569         q.removeAt(i);
    570         assertTrue("Remove at " + i + " perm " + perm, q.isIntact());
    571       }
    572     }
    573   }
    574 
    575   /**
    576    * Regression test for bug found.
    577    */
    578   public void testCorrectOrdering_regression() {
    579     MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue
    580         .create(ImmutableList.of(3, 5, 1, 4, 7));
    581     List<Integer> expected = ImmutableList.of(1, 3, 4, 5, 7);
    582     List<Integer> actual = new ArrayList<Integer>(5);
    583     for (int i = 0; i < expected.size(); i++) {
    584       actual.add(q.pollFirst());
    585     }
    586     assertEquals(expected, actual);
    587   }
    588 
    589   public void testCorrectOrdering_smallHeapsPollFirst() {
    590     for (int size = 2; size < 16; size++) {
    591       for (int attempts = 0; attempts < size * (size - 1); attempts++) {
    592         ArrayList<Integer> elements = createOrderedList(size);
    593         List<Integer> expected = ImmutableList.copyOf(elements);
    594         MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create();
    595         long seed = insertRandomly(elements, q);
    596         while (!q.isEmpty()) {
    597           elements.add(q.pollFirst());
    598         }
    599         assertEquals("Using seed " + seed, expected, elements);
    600       }
    601     }
    602   }
    603 
    604   public void testCorrectOrdering_smallHeapsPollLast() {
    605     for (int size = 2; size < 16; size++) {
    606       for (int attempts = 0; attempts < size * (size - 1); attempts++) {
    607         ArrayList<Integer> elements = createOrderedList(size);
    608         List<Integer> expected = ImmutableList.copyOf(elements);
    609         MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create();
    610         long seed = insertRandomly(elements, q);
    611         while (!q.isEmpty()) {
    612           elements.add(0, q.pollLast());
    613         }
    614         assertEquals("Using seed " + seed, expected, elements);
    615       }
    616     }
    617   }
    618 
    619   public void testCorrectOrdering_mediumHeapsPollFirst() {
    620     for (int attempts = 0; attempts < 5000; attempts++) {
    621       int size = new Random().nextInt(256) + 16;
    622       ArrayList<Integer> elements = createOrderedList(size);
    623       List<Integer> expected = ImmutableList.copyOf(elements);
    624       MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create();
    625       long seed = insertRandomly(elements, q);
    626       while (!q.isEmpty()) {
    627         elements.add(q.pollFirst());
    628       }
    629       assertEquals("Using seed " + seed, expected, elements);
    630     }
    631   }
    632 
    633   /**
    634    * Regression test for bug found in random testing.
    635    */
    636   public void testCorrectOrdering_73ElementBug() {
    637     int size = 73;
    638     long seed = 7522346378524621981L;
    639     ArrayList<Integer> elements = createOrderedList(size);
    640     List<Integer> expected = ImmutableList.copyOf(elements);
    641     MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create();
    642     insertRandomly(elements, q, new Random(seed));
    643     assertTrue(q.isIntact());
    644     while (!q.isEmpty()) {
    645       elements.add(q.pollFirst());
    646       assertTrue("State " + Arrays.toString(q.toArray()), q.isIntact());
    647     }
    648     assertEquals("Using seed " + seed, expected, elements);
    649   }
    650 
    651   public void testCorrectOrdering_mediumHeapsPollLast() {
    652     for (int attempts = 0; attempts < 5000; attempts++) {
    653       int size = new Random().nextInt(256) + 16;
    654       ArrayList<Integer> elements = createOrderedList(size);
    655       List<Integer> expected = ImmutableList.copyOf(elements);
    656       MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create();
    657       long seed = insertRandomly(elements, q);
    658       while (!q.isEmpty()) {
    659         elements.add(0, q.pollLast());
    660       }
    661       assertEquals("Using seed " + seed, expected, elements);
    662     }
    663   }
    664 
    665   public void testCorrectOrdering_randomAccess() {
    666     long seed = new Random().nextLong();
    667     Random random = new Random(seed);
    668     PriorityQueue<Integer> control = new PriorityQueue<Integer>();
    669     MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create();
    670     for (int i = 0; i < 73; i++) { // 73 is a childless uncle case.
    671       Integer element = random.nextInt();
    672       control.add(element);
    673       assertTrue(q.add(element));
    674     }
    675     assertTrue("State " + Arrays.toString(q.toArray()), q.isIntact());
    676     for (int i = 0; i < 500000; i++) {
    677       if (random.nextBoolean()) {
    678         Integer element = random.nextInt();
    679         control.add(element);
    680         q.add(element);
    681       } else {
    682         assertEquals("Using seed " + seed, control.poll(), q.pollFirst());
    683       }
    684     }
    685     while (!control.isEmpty()) {
    686       assertEquals("Using seed " + seed, control.poll(), q.pollFirst());
    687     }
    688     assertTrue(q.isEmpty());
    689   }
    690 
    691   public void testExhaustive_pollAndPush() {
    692     int size = 8;
    693     List<Integer> expected = createOrderedList(size);
    694     for (Collection<Integer> perm : Collections2.permutations(expected)) {
    695       MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(perm);
    696       List<Integer> elements = Lists.newArrayListWithCapacity(size);
    697       while (!q.isEmpty()) {
    698         Integer next = q.pollFirst();
    699         for (int i = 0; i <= size; i++) {
    700           assertTrue(q.add(i));
    701           assertTrue(q.add(next));
    702           assertTrue(q.remove(i));
    703           assertEquals(next, q.poll());
    704         }
    705         elements.add(next);
    706       }
    707       assertEquals("Started with " + perm, expected, elements);
    708     }
    709   }
    710 
    711   /**
    712    * Regression test for b/4124577
    713    */
    714   public void testRegression_dataCorruption() {
    715     int size = 8;
    716     List<Integer> expected = createOrderedList(size);
    717     MinMaxPriorityQueue<Integer> q = MinMaxPriorityQueue.create(expected);
    718     List<Integer> contents = Lists.newArrayList(expected);
    719     List<Integer> elements = Lists.newArrayListWithCapacity(size);
    720     while (!q.isEmpty()) {
    721       assertThat(q).has().exactlyAs(contents);
    722       Integer next = q.pollFirst();
    723       contents.remove(next);
    724       assertThat(q).has().exactlyAs(contents);
    725       for (int i = 0; i <= size; i++) {
    726         q.add(i);
    727         contents.add(i);
    728         assertThat(q).has().exactlyAs(contents);
    729         q.add(next);
    730         contents.add(next);
    731         assertThat(q).has().exactlyAs(contents);
    732         q.remove(i);
    733         assertTrue(contents.remove(Integer.valueOf(i)));
    734         assertThat(q).has().exactlyAs(contents);
    735         assertEquals(next, q.poll());
    736         contents.remove(next);
    737         assertThat(q).has().exactlyAs(contents);
    738       }
    739       elements.add(next);
    740     }
    741     assertEquals(expected, elements);
    742   }
    743 
    744   /**
    745    * Returns the seed used for the randomization.
    746    */
    747   private long insertRandomly(ArrayList<Integer> elements,
    748       MinMaxPriorityQueue<Integer> q) {
    749     long seed = new Random().nextLong();
    750     Random random = new Random(seed);
    751     insertRandomly(elements, q, random);
    752     return seed;
    753   }
    754 
    755   private void insertRandomly(ArrayList<Integer> elements, MinMaxPriorityQueue<Integer> q,
    756       Random random) {
    757     while (!elements.isEmpty()) {
    758       int selectedIndex = random.nextInt(elements.size());
    759       q.offer(elements.remove(selectedIndex));
    760     }
    761   }
    762 
    763   private ArrayList<Integer> createOrderedList(int size) {
    764     ArrayList<Integer> elements = new ArrayList<Integer>(size);
    765     for (int i = 0; i < size; i++) {
    766       elements.add(i);
    767     }
    768     return elements;
    769   }
    770 
    771   public void testIsEvenLevel() {
    772     assertTrue(MinMaxPriorityQueue.isEvenLevel(0));
    773     assertFalse(MinMaxPriorityQueue.isEvenLevel(1));
    774     assertFalse(MinMaxPriorityQueue.isEvenLevel(2));
    775     assertTrue(MinMaxPriorityQueue.isEvenLevel(3));
    776 
    777     assertFalse(MinMaxPriorityQueue.isEvenLevel((1 << 10) - 2));
    778     assertTrue(MinMaxPriorityQueue.isEvenLevel((1 << 10) - 1));
    779 
    780     int i = 1 << 29;
    781     assertTrue(MinMaxPriorityQueue.isEvenLevel(i - 2));
    782     assertFalse(MinMaxPriorityQueue.isEvenLevel(i - 1));
    783     assertFalse(MinMaxPriorityQueue.isEvenLevel(i));
    784 
    785     i = 1 << 30;
    786     assertFalse(MinMaxPriorityQueue.isEvenLevel(i - 2));
    787     assertTrue(MinMaxPriorityQueue.isEvenLevel(i - 1));
    788     assertTrue(MinMaxPriorityQueue.isEvenLevel(i));
    789 
    790     // 1 << 31 is negative because of overflow, 1 << 31 - 1 is positive
    791     // since isEvenLevel adds 1, we need to do - 2.
    792     assertTrue(MinMaxPriorityQueue.isEvenLevel((1 << 31) - 2));
    793     assertTrue(MinMaxPriorityQueue.isEvenLevel(Integer.MAX_VALUE - 1));
    794     try {
    795       MinMaxPriorityQueue.isEvenLevel((1 << 31) - 1);
    796       fail("Should overflow");
    797     } catch (IllegalStateException e) {
    798       // expected
    799     }
    800     try {
    801       MinMaxPriorityQueue.isEvenLevel(Integer.MAX_VALUE);
    802       fail("Should overflow");
    803     } catch (IllegalStateException e) {
    804       // expected
    805     }
    806     try {
    807       MinMaxPriorityQueue.isEvenLevel(1 << 31);
    808       fail("Should be negative");
    809     } catch (IllegalStateException e) {
    810       // expected
    811     }
    812     try {
    813       MinMaxPriorityQueue.isEvenLevel(Integer.MIN_VALUE);
    814       fail("Should be negative");
    815     } catch (IllegalStateException e) {
    816       // expected
    817     }
    818   }
    819 
    820   public void testNullPointers() {
    821     NullPointerTester tester = new NullPointerTester();
    822     tester.testAllPublicConstructors(MinMaxPriorityQueue.class);
    823     tester.testAllPublicStaticMethods(MinMaxPriorityQueue.class);
    824     tester.testAllPublicInstanceMethods(MinMaxPriorityQueue.<String>create());
    825   }
    826 
    827   private static void insertIntoReplica(
    828       Map<Integer, AtomicInteger> replica,
    829       int newValue) {
    830     if (replica.containsKey(newValue)) {
    831       replica.get(newValue).incrementAndGet();
    832     } else {
    833       replica.put(newValue, new AtomicInteger(1));
    834     }
    835   }
    836 
    837   private static void removeMinFromReplica(
    838       SortedMap<Integer, AtomicInteger> replica,
    839       int minValue) {
    840     Integer replicatedMinValue = replica.firstKey();
    841     assertEquals(replicatedMinValue, (Integer) minValue);
    842     removeFromReplica(replica, replicatedMinValue);
    843   }
    844 
    845   private static void removeMaxFromReplica(
    846       SortedMap<Integer, AtomicInteger> replica,
    847       int maxValue) {
    848     Integer replicatedMaxValue = replica.lastKey();
    849     assertTrue("maxValue is incorrect", replicatedMaxValue == maxValue);
    850     removeFromReplica(replica, replicatedMaxValue);
    851   }
    852 
    853   private static void removeFromReplica(
    854       Map<Integer, AtomicInteger> replica, int value) {
    855     AtomicInteger numOccur = replica.get(value);
    856     if (numOccur.decrementAndGet() == 0) {
    857       replica.remove(value);
    858     }
    859   }
    860 }
    861