blob: 9c5a843071d08439eb571bde1df42d9d80073e21 [file] [log] [blame]
/*
* Copyright (c) 2019-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "TestHarness.h"
#include "pas_bootstrap_free_heap.h"
#include "pas_min_heap.h"
#include <set>
#include <vector>
using namespace std;
namespace {
constexpr bool verbose = false;
struct Element {
Element() = default;
Element(unsigned char c)
: c(c)
{
}
unsigned char c { 0 };
size_t index { 0 };
};
unsigned char asChar(void* value)
{
return static_cast<Element*>(value)->c;
}
int compare(Element** left, Element** right)
{
if (asChar(*left) < asChar(*right))
return -1;
if (asChar(*left) == asChar(*right))
return 0;
return 1;
}
size_t getIndex(Element** element)
{
return (*element)->index;
}
void setIndex(Element** element, size_t index)
{
(*element)->index = index;
}
PAS_CREATE_MIN_HEAP(
MinHeap,
Element*,
5,
.compare = compare,
.get_index = getIndex,
.set_index = setIndex);
void dumpHeap(MinHeap& minHeap)
{
cout << " min heap:\n";
if (!minHeap.size) {
cout << " <empty>\n";
return;
}
// Guide:
// x
// x x
// x x x x
// x x x x x x x x
size_t numLevels = pas_log2(minHeap.size) + 1;
size_t numColumns = 1 << (numLevels - 1);
size_t index = 1;
for (size_t levelIndex = 0; levelIndex < numLevels; ++levelIndex) {
cout << " ";
size_t numElements = 1 << levelIndex;
for (size_t elementIndex = 0; elementIndex < numElements; elementIndex++) {
size_t numSpaces = numColumns/numElements * 2 - 1;
if (!elementIndex)
numSpaces /= 2;
for (size_t spaceIndex = numSpaces; spaceIndex--;)
cout << " ";
if (index <= minHeap.size)
cout << (*MinHeap_get_ptr_by_index(&minHeap, index++))->c;
else
PAS_ASSERT(levelIndex + 1 == numLevels);
}
cout << "\n";
}
PAS_ASSERT(index == minHeap.size + 1);
}
void testMinHeap(const char* program)
{
MinHeap minHeap;
MinHeap_construct(&minHeap);
for (size_t i = 0; program[i]; ++i) {
char command = program[i];
char value = 0;
auto getValue = [&] () {
value = program[++i];
PAS_ASSERT(value);
};
switch (command) {
case '+': {
getValue();
Element* thing = new Element(value);
MinHeap_add(&minHeap, thing, &allocationConfig);
CHECK(thing->index);
if (verbose) {
cout << " After +" << value << ":\n";
dumpHeap(minHeap);
}
break;
}
case '!': {
CHECK(!MinHeap_take_min(&minHeap));
break;
}
case '-': {
getValue();
if (verbose) {
cout << " Before -" << value << ":\n";
dumpHeap(minHeap);
}
Element* thing = MinHeap_take_min(&minHeap);
CHECK_EQUAL(thing->c, value);
CHECK(!thing->index);
break;
}
case '*': {
getValue();
if (verbose) {
cout << " Before *" << value << ":\n";
dumpHeap(minHeap);
}
size_t foundIndex = 0;
bool found = false;
for (size_t i = minHeap.size; i; i--) {
Element* element = *MinHeap_get_ptr_by_index(&minHeap, i);
if (element->c == value) {
CHECK(!found);
foundIndex = i;
found = true;
}
}
Element* element = MinHeap_remove_by_index(&minHeap, foundIndex);
CHECK_EQUAL(element->c, value);
break;
}
default:
PAS_ASSERT(!"Should not be reached");
}
}
MinHeap_destruct(&minHeap, &allocationConfig);
}
void minHeapChaos(unsigned initialSize, unsigned numOperations,
bool randomRemove,
const pas_allocation_config& myAllocationConfig)
{
MinHeap minHeap;
MinHeap_construct(&minHeap);
multiset<unsigned char> standard;
for (unsigned i = initialSize; i--;) {
char c = static_cast<char>(deterministicRandomNumber(256));
Element* thing = new Element(c);
MinHeap_add(&minHeap, thing, &myAllocationConfig);
standard.insert(c);
}
CHECK_EQUAL(static_cast<unsigned>(MinHeap_get_min(&minHeap)->c),
static_cast<unsigned>(*standard.begin()));
for (unsigned i = numOperations; i--;) {
CHECK_EQUAL(minHeap.size, standard.size());
switch (deterministicRandomNumber(2)) {
case 0: {
char c = static_cast<char>(deterministicRandomNumber(256));
Element* thing = new Element(c);
MinHeap_add(&minHeap, thing, &myAllocationConfig);
standard.insert(c);
break;
}
case 1: {
if (standard.empty()) {
PAS_ASSERT(!minHeap.size);
CHECK(!MinHeap_take_min(&minHeap));
break;
}
switch (randomRemove ? deterministicRandomNumber(2) : 0) {
case 0: {
Element* thing = MinHeap_take_min(&minHeap);
CHECK(thing);
auto iter = standard.begin();
CHECK_EQUAL(static_cast<unsigned>(thing->c), static_cast<unsigned>(*iter));
standard.erase(iter);
break;
}
case 1: {
Element* thing = *MinHeap_get_ptr_by_index(
&minHeap,
1 + deterministicRandomNumber(static_cast<unsigned>(minHeap.size)));
standard.erase(standard.find(thing->c));
MinHeap_remove(&minHeap, thing);
break;
}
default:
PAS_ASSERT(!"Should not be reached");
break;
}
break;
}
default:
PAS_ASSERT(!"Should not be reached");
break;
}
}
for (;;) {
CHECK_EQUAL(minHeap.size, standard.size());
if (!minHeap.size)
break;
Element* thing = MinHeap_take_min(&minHeap);
CHECK(thing);
auto iter = standard.begin();
CHECK_EQUAL(static_cast<unsigned>(thing->c), static_cast<unsigned>(*iter));
standard.erase(iter);
}
}
void testMinHeapChaos(unsigned initialSize, unsigned numOperations, bool randomRemove)
{
minHeapChaos(initialSize, numOperations, randomRemove, allocationConfig);
}
pas_allocation_config makeBootstrapAllocationConfig()
{
pas_allocation_config result;
pas_bootstrap_free_heap_allocation_config_construct(&result, pas_lock_is_not_held);
return result;
}
void testBootstrapMinHeapChaos(unsigned initialSize, unsigned numOperations, bool randomRemove)
{
minHeapChaos(initialSize, numOperations, randomRemove, makeBootstrapAllocationConfig());
}
} // anonymous namespace
void addMinHeapTests()
{
ADD_TEST(testMinHeap("!"));
ADD_TEST(testMinHeap("+a"));
ADD_TEST(testMinHeap("+a-a"));
ADD_TEST(testMinHeap("!+a-a!"));
ADD_TEST(testMinHeap("+a+b+c+d+e+f+g+h+i+j+k+l+m+n+i+o+p-a-b-c-d-e-f-g-h-i-i-j-k-l-m-n-o-p!"));
ADD_TEST(testMinHeap("+j+b+d+l+o+f+p+n+i+a+k+c+m+h+i+e+g-a-b-c-d-e-f-g-h-i-i-j-k-l-m-n-o-p!"));
ADD_TEST(testMinHeap("+a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+p+q+r+s+t+u+v+w+x+y+z*z*y*x*w*v*u*t*s*r*q*p*o*n*m*l*k*j*i*h*g*f*e*d*c*b*a"));
ADD_TEST(testMinHeap("+a+w+c+d+e+f+p+h+i+j+k+l+m+n+o+g+q+r+s+t+u+v+b+x+y+z*z*y*x*w*v*u*t*s*r*q*p*o*n*m*l*k*j*i*h*g*f*e*d*c*b*a"));
ADD_TEST(testMinHeap("+a+w+c+d+e+f+p+h+i+j+k+l+m+n+o+g+q+r+s+t+u+v+b+x+y+z*a*y*c*w*e*u*g*s*r*q*p*o*n*m*l*k*j*i*h*t*f*v*d*x*b*z"));
ADD_TEST(testMinHeap("+a+w+c+d+e+f+p+h+i+j+k+l+m+n+o+g+q+r+s+t+u+v+b+x+y+z*a*y*c*w*e*u*g*s*r*q*p*o*n*m*l-b-d-f-h-i-j-k-t-v-x-z"));
ADD_TEST(testMinHeap("+z+y+x+w+v+u+t+s+r+q+p+o+n+m+l+k+j+i+h+g+f+e+d+c+b+a*a*y*c*w*e*u*g*s*r*q*p*o*n*m*l-b-d-f-h-i-j-k-t-v-x-z"));
ADD_TEST(testMinHeap("+z+y+x+w+v+u+t+s+r+q+p+o+n+m+l+k+j+i+h+g+f+e+d+c+b+a*u*p*y*k-a-b-c-d-e-f-g-h-i-j-l-m-n-o-q-r-s-t-v-w-x-z"));
ADD_TEST(testMinHeap("+z+y+x+w+v+u+t+s+r+q+p+o+n+m+l+k+j+i+h+g+f+e+d+c+b+a*u*p*y*q-a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-r-s-t-v-w-x-z"));
ADD_TEST(testMinHeap("+a+b+c+d*d-a-b-c!"));
ADD_TEST(testMinHeapChaos(100, 1000, false));
ADD_TEST(testMinHeapChaos(1000, 10000, false));
ADD_TEST(testMinHeapChaos(10000, 10000, false));
ADD_TEST(testBootstrapMinHeapChaos(100, 1000, false));
ADD_TEST(testBootstrapMinHeapChaos(1000, 10000, false));
ADD_TEST(testBootstrapMinHeapChaos(10000, 10000, false));
ADD_TEST(testMinHeapChaos(100, 1000, true));
ADD_TEST(testMinHeapChaos(1000, 10000, true));
ADD_TEST(testMinHeapChaos(10000, 10000, true));
ADD_TEST(testBootstrapMinHeapChaos(100, 1000, true));
ADD_TEST(testBootstrapMinHeapChaos(1000, 10000, true));
ADD_TEST(testBootstrapMinHeapChaos(10000, 10000, true));
ADD_TEST(testBootstrapMinHeapChaos(10000, 100000, true));
ADD_TEST(testBootstrapMinHeapChaos(100000, 100000, true));
}