blob: d4859cc0a99dab8fc906cf1a057b947821b3d022 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Random;
public class TestPriorityQueue extends LuceneTestCase {
private static class IntegerQueue extends PriorityQueue<Integer> {
public IntegerQueue(int count) {
super(count);
}
@Override
protected boolean lessThan(Integer a, Integer b) {
return (a < b);
}
protected final void checkValidity() {
Object[] heapArray = getHeapArray();
for (int i = 1; i <= size(); i++) {
int parent = i >>> 1;
if (parent > 1) {
if (lessThan((Integer) heapArray[parent], (Integer) heapArray[i]) == false) {
assertEquals(heapArray[parent], heapArray[i]);
}
}
}
}
}
public void testPQ() throws Exception {
testPQ(atLeast(10000), random());
}
public static void testPQ(int count, Random gen) {
PriorityQueue<Integer> pq = new IntegerQueue(count);
int sum = 0, sum2 = 0;
for (int i = 0; i < count; i++) {
int next = gen.nextInt();
sum += next;
pq.add(next);
}
// Date end = new Date();
// System.out.print(((float)(end.getTime()-start.getTime()) / count) * 1000);
// System.out.println(" microseconds/put");
// start = new Date();
int last = Integer.MIN_VALUE;
for (int i = 0; i < count; i++) {
Integer next = pq.pop();
assertTrue(next.intValue() >= last);
last = next.intValue();
sum2 += last;
}
assertEquals(sum, sum2);
// end = new Date();
// System.out.print(((float)(end.getTime()-start.getTime()) / count) * 1000);
// System.out.println(" microseconds/pop");
}
public void testClear() {
PriorityQueue<Integer> pq = new IntegerQueue(3);
pq.add(2);
pq.add(3);
pq.add(1);
assertEquals(3, pq.size());
pq.clear();
assertEquals(0, pq.size());
}
public void testFixedSize() {
PriorityQueue<Integer> pq = new IntegerQueue(3);
pq.insertWithOverflow(2);
pq.insertWithOverflow(3);
pq.insertWithOverflow(1);
pq.insertWithOverflow(5);
pq.insertWithOverflow(7);
pq.insertWithOverflow(1);
assertEquals(3, pq.size());
assertEquals((Integer) 3, pq.top());
}
public void testInsertWithOverflow() {
int size = 4;
PriorityQueue<Integer> pq = new IntegerQueue(size);
Integer i1 = 2;
Integer i2 = 3;
Integer i3 = 1;
Integer i4 = 5;
Integer i5 = 7;
Integer i6 = 1;
assertNull(pq.insertWithOverflow(i1));
assertNull(pq.insertWithOverflow(i2));
assertNull(pq.insertWithOverflow(i3));
assertNull(pq.insertWithOverflow(i4));
assertTrue(pq.insertWithOverflow(i5) == i3); // i3 should have been dropped
assertTrue(pq.insertWithOverflow(i6) == i6); // i6 should not have been inserted
assertEquals(size, pq.size());
assertEquals((Integer) 2, pq.top());
}
public void testRemovalsAndInsertions() {
Random random = random();
int numDocsInPQ = TestUtil.nextInt(random, 1, 100);
IntegerQueue pq = new IntegerQueue(numDocsInPQ);
Integer lastLeast = null;
// Basic insertion of new content
ArrayList<Integer> sds = new ArrayList<Integer>(numDocsInPQ);
for (int i = 0; i < numDocsInPQ * 10; i++) {
Integer newEntry = Math.abs(random.nextInt());
sds.add(newEntry);
Integer evicted = pq.insertWithOverflow(newEntry);
pq.checkValidity();
if (evicted != null) {
assertTrue(sds.remove(evicted));
if (evicted != newEntry) {
assertTrue(evicted == lastLeast);
}
}
Integer newLeast = pq.top();
if ((lastLeast != null) && (newLeast != newEntry)
&& (newLeast != lastLeast)) {
// If there has been a change of least entry and it wasn't our new
// addition we expect the scores to increase
assertTrue(newLeast <= newEntry);
assertTrue(newLeast >= lastLeast);
}
lastLeast = newLeast;
}
// Try many random additions to existing entries - we should always see
// increasing scores in the lowest entry in the PQ
for (int p = 0; p < 500000; p++) {
int element = (int) (random.nextFloat() * (sds.size() - 1));
Integer objectToRemove = sds.get(element);
assertTrue(sds.remove(element) == objectToRemove);
assertTrue(pq.remove(objectToRemove));
pq.checkValidity();
Integer newEntry = Math.abs(random.nextInt());
sds.add(newEntry);
assertNull(pq.insertWithOverflow(newEntry));
pq.checkValidity();
Integer newLeast = pq.top();
if ((objectToRemove != lastLeast) && (lastLeast != null)
&& (newLeast != newEntry)) {
// If there has been a change of least entry and it wasn't our new
// addition or the loss of our randomly removed entry we expect the
// scores to increase
assertTrue(newLeast <= newEntry);
assertTrue(newLeast >= lastLeast);
}
lastLeast = newLeast;
}
}
public void testIteratorEmpty() {
IntegerQueue queue = new IntegerQueue(3);
Iterator<Integer> it = queue.iterator();
assertFalse(it.hasNext());
expectThrows(NoSuchElementException.class, () -> {
it.next();
});
}
public void testIteratorOne() {
IntegerQueue queue = new IntegerQueue(3);
queue.add(1);
Iterator<Integer> it = queue.iterator();
assertTrue(it.hasNext());
assertEquals(Integer.valueOf(1), it.next());
assertFalse(it.hasNext());
expectThrows(NoSuchElementException.class, () -> {
it.next();
});
}
public void testIteratorTwo() {
IntegerQueue queue = new IntegerQueue(3);
queue.add(1);
queue.add(2);
Iterator<Integer> it = queue.iterator();
assertTrue(it.hasNext());
assertEquals(Integer.valueOf(1), it.next());
assertTrue(it.hasNext());
assertEquals(Integer.valueOf(2), it.next());
assertFalse(it.hasNext());
expectThrows(NoSuchElementException.class, () -> {
it.next();
});
}
public void testIteratorRandom() {
final int maxSize = TestUtil.nextInt(random(), 1, 20);
IntegerQueue queue = new IntegerQueue(maxSize);
final int iters = atLeast(100);
final List<Integer> expected = new ArrayList<>();
for (int iter = 0; iter < iters; ++iter) {
if (queue.size() == 0 || (queue.size() < maxSize && random().nextBoolean())) {
final Integer value = random().nextInt(10);
queue.add(value);
expected.add(value);
} else {
expected.remove(queue.pop());
}
List<Integer> actual = new ArrayList<>();
for (Integer value : queue) {
actual.add(value);
}
CollectionUtil.introSort(expected);
CollectionUtil.introSort(actual);
assertEquals(expected, actual);
}
}
public void testMaxIntSize() {
expectThrows(IllegalArgumentException.class, () -> {
new PriorityQueue<Boolean>(Integer.MAX_VALUE) {
@Override
public boolean lessThan(Boolean a, Boolean b) {
// uncalled
return true;
}
};
});
}
}