planetiler/planetiler-core/src/test/java/com/onthegomap/planetiler/collection/LongMinHeapTest.java

401 wiersze
10 KiB
Java

/*
* Licensed to GraphHopper GmbH under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* GraphHopper GmbH 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 com.onthegomap.planetiler.collection;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.carrotsearch.hppc.IntArrayList;
import com.carrotsearch.hppc.IntHashSet;
import com.carrotsearch.hppc.IntSet;
import java.util.PriorityQueue;
import java.util.Random;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
/**
* Ported from <a href=
* "https://github.com/graphhopper/graphhopper/blob/master/core/src/test/java/com/graphhopper/coll/MinHeapWithUpdateTest.java">GraphHopper</a>
* and modified to use long instead of float values, use stable random seed for reproducibility, and to use new
* implementations.
*/
class LongMinHeapTest {
protected LongMinHeap heap;
void create(int capacity) {
heap = LongMinHeap.newArrayHeap(capacity, Integer::compare);
}
@Test
void outOfRange() {
create(4);
assertThrows(IllegalArgumentException.class, () -> heap.push(4, 12L));
assertThrows(IllegalArgumentException.class, () -> heap.push(-1, 12L));
}
@Test
void tooManyElements() {
create(3);
heap.push(1, 1L);
heap.push(2, 1L);
heap.push(0, 1L);
// pushing element 1 again is not allowed (but this is not checked explicitly). however pushing more elements
// than 3 is already an error
assertThrows(IllegalStateException.class, () -> heap.push(1, 1L));
assertThrows(IllegalStateException.class, () -> heap.push(2, 61L));
}
@Test
void duplicateElements() {
create(5);
heap.push(1, 2L);
heap.push(0, 4L);
heap.push(2, 1L);
assertEquals(2, heap.poll());
// pushing 2 again is ok because it was polled before
heap.push(2, 6L);
// but now its not ok to push it again
assertThrows(IllegalStateException.class, () -> heap.push(2, 4L));
}
@ParameterizedTest
@CsvSource({
"0, 1, 2, 3, 4, 5, 6, 7",
"7, 6, 5, 4, 3, 2, 1, 0",
"0, 1, 2, 6, 7, 5, 4, 3",
"0, 1, 5, 2, 4, 3, 6, 7",
"0, 5, 6, 7, 1, 2, 4, 3",
"5, 0, 1, 2, 7, 6, 4, 3",
})
void tieBreaker(int a, int b, int c, int d, int e, int f, int g, int h) {
heap = LongMinHeap.newArrayHeap(9, (id1, id2) -> -Integer.compare(id1, id2));
heap.push(a, 0L);
heap.push(b, 0L);
heap.push(c, 0L);
heap.push(d, 0L);
heap.push(e, 0L);
heap.push(f, 0L);
heap.push(g, 0L);
heap.push(h, 0L);
assertEquals(7, heap.poll());
assertEquals(6, heap.poll());
assertEquals(5, heap.poll());
assertEquals(4, heap.poll());
assertEquals(3, heap.poll());
assertEquals(2, heap.poll());
assertEquals(1, heap.poll());
assertEquals(0, heap.poll());
}
@Test
void testContains() {
create(4);
heap.push(1, 1L);
heap.push(2, 7L);
heap.push(0, 5L);
assertFalse(heap.contains(3));
assertTrue(heap.contains(1));
assertEquals(1, heap.poll());
assertFalse(heap.contains(1));
}
@Test
void containsAfterClear() {
create(4);
heap.push(1, 1L);
heap.push(2, 1L);
assertEquals(2, heap.size());
heap.clear();
assertFalse(heap.contains(0));
assertFalse(heap.contains(1));
assertFalse(heap.contains(2));
}
@Test
void testSize() {
create(10);
assertEquals(0, heap.size());
assertTrue(heap.isEmpty());
heap.push(9, 36L);
heap.push(5, 23L);
heap.push(3, 23L);
assertEquals(3, heap.size());
assertFalse(heap.isEmpty());
}
@Test
void testClear() {
create(5);
assertTrue(heap.isEmpty());
heap.push(3, 12L);
heap.push(4, 3L);
assertEquals(2, heap.size());
heap.clear();
assertTrue(heap.isEmpty());
heap.push(4, 63L);
heap.push(1, 21L);
assertEquals(2, heap.size());
assertEquals(1, heap.peekId());
assertEquals(21L, heap.peekValue());
assertEquals(1, heap.poll());
assertEquals(4, heap.poll());
assertTrue(heap.isEmpty());
}
@Test
void testPush() {
create(5);
heap.push(4, 63L);
heap.push(1, 21L);
assertEquals(2, heap.size());
assertEquals(1, heap.peekId());
assertEquals(21L, heap.peekValue());
assertEquals(1, heap.poll());
assertEquals(4, heap.poll());
assertTrue(heap.isEmpty());
}
@Test
void testPeek() {
create(5);
heap.push(4, -16L);
heap.push(2, 13L);
heap.push(1, -51L);
heap.push(3, 4L);
assertEquals(1, heap.peekId());
assertEquals(-51L, heap.peekValue());
}
@Test
void pushAndPoll() {
create(10);
heap.push(9, 36L);
heap.push(5, 23L);
heap.push(3, 23L);
assertEquals(3, heap.size());
heap.poll();
assertEquals(2, heap.size());
heap.poll();
heap.poll();
assertTrue(heap.isEmpty());
}
@Test
void pollSorted() {
create(10);
heap.push(9, 36L);
heap.push(5, 21L);
heap.push(3, 23L);
heap.push(8, 57L);
heap.push(7, 22L);
IntArrayList polled = new IntArrayList();
while (!heap.isEmpty()) {
polled.add(heap.poll());
}
assertEquals(IntArrayList.from(5, 7, 3, 9, 8), polled);
}
@Test
void poll() {
create(10);
assertTrue(heap.isEmpty());
assertEquals(0, heap.size());
heap.push(9, 36L);
assertFalse(heap.isEmpty());
assertEquals(1, heap.size());
heap.push(5, 21L);
assertFalse(heap.isEmpty());
assertEquals(2, heap.size());
heap.push(3, 23L);
assertFalse(heap.isEmpty());
assertEquals(3, heap.size());
heap.push(8, 57L);
assertFalse(heap.isEmpty());
assertEquals(4, heap.size());
assertEquals(5, heap.poll());
assertFalse(heap.isEmpty());
assertEquals(3, heap.size());
assertEquals(3, heap.poll());
assertFalse(heap.isEmpty());
assertEquals(2, heap.size());
assertEquals(9, heap.poll());
assertFalse(heap.isEmpty());
assertEquals(1, heap.size());
assertEquals(8, heap.poll());
assertTrue(heap.isEmpty());
assertEquals(0, heap.size());
}
@Test
void clear() {
create(10);
heap.push(9, 36L);
heap.push(5, 21L);
heap.push(3, 23L);
heap.clear();
assertTrue(heap.isEmpty());
assertEquals(0, heap.size());
}
@Test
void poll100Ascending() {
create(100);
for (int i = 1; i < 100; i++) {
heap.push(i, i);
}
for (int i = 1; i < 100; i++) {
assertEquals(i, heap.poll());
}
}
@Test
void poll100Descending() {
create(100);
for (int i = 99; i >= 1; i--) {
heap.push(i, i);
}
for (int i = 1; i < 100; i++) {
assertEquals(i, heap.poll());
}
}
@Test
void update() {
create(10);
heap.push(9, 36L);
heap.push(5, 21L);
heap.push(3, 23L);
heap.update(3, 1L);
assertEquals(3, heap.peekId());
heap.update(3, 100L);
assertEquals(5, heap.peekId());
heap.update(9, -13L);
assertEquals(9, heap.peekId());
assertEquals(-13L, heap.peekValue());
IntArrayList polled = new IntArrayList();
while (!heap.isEmpty()) {
polled.add(heap.poll());
}
assertEquals(IntArrayList.from(9, 5, 3), polled);
}
@Test
void updateHead() {
create(10);
heap.push(1, 1);
heap.push(2, 2);
heap.push(3, 3);
heap.push(4, 4);
heap.push(5, 5);
heap.updateHead(6);
heap.updateHead(7);
heap.updateHead(8);
IntArrayList polled = new IntArrayList();
while (!heap.isEmpty()) {
polled.add(heap.poll());
}
assertEquals(IntArrayList.from(4, 5, 1, 2, 3), polled);
}
@Test
void randomPushsThenPolls() {
Random rnd = new Random(0);
int size = 1 + rnd.nextInt(100);
PriorityQueue<Entry> pq = new PriorityQueue<>(size);
create(size);
IntSet set = new IntHashSet();
while (pq.size() < size) {
int id = rnd.nextInt(size);
if (!set.add(id))
continue;
long val = (long) (Long.MAX_VALUE * rnd.nextFloat());
pq.add(new Entry(id, val));
heap.push(id, val);
}
while (!pq.isEmpty()) {
Entry entry = pq.poll();
assertEquals(entry.val, heap.peekValue());
assertEquals(entry.id, heap.poll());
assertEquals(pq.size(), heap.size());
}
}
@Test
void randomPushsAndPolls() {
Random rnd = new Random(0);
int size = 1 + rnd.nextInt(100);
PriorityQueue<Entry> pq = new PriorityQueue<>(size);
create(size);
IntSet set = new IntHashSet();
int pushCount = 0;
for (int i = 0; i < 1000; i++) {
boolean push = pq.isEmpty() || (rnd.nextBoolean());
if (push) {
int id = rnd.nextInt(size);
if (!set.add(id))
continue;
long val = (long) (Long.MAX_VALUE * rnd.nextFloat());
pq.add(new Entry(id, val));
heap.push(id, val);
pushCount++;
} else {
Entry entry = pq.poll();
assert entry != null;
assertEquals(entry.val, heap.peekValue());
assertEquals(entry.id, heap.poll());
assertEquals(pq.size(), heap.size());
set.removeAll(entry.id);
}
}
assertTrue(pushCount > 0);
}
static class Entry implements Comparable<Entry> {
int id;
long val;
public Entry(int id, long val) {
this.id = id;
this.val = val;
}
@Override
public int compareTo(Entry o) {
return Long.compare(val, o.val);
}
}
}