blob: 2e5d351c4f2d018076ca3274761f01d15d07e2d8 [file] [log] [blame]
/*
* Copyright (C) 2011-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.
* 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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.
*/
// This is based on WebKit's TestWebkitAPI test for WTF's RedBlackTree.
#include "TestHarness.h"
#include <algorithm>
#include "pas_cartesian_tree.h"
#include "pas_heap_lock.h"
#include "pas_utility_heap.h"
#include <sstream>
#include <vector>
using namespace std;
namespace {
static constexpr bool verbose = false;
struct TestNode : public pas_cartesian_tree_node {
TestNode(char xKey, char yKey, unsigned value)
: xKey(xKey)
, yKey(yKey)
, value(value)
{
}
char xKey;
char yKey;
unsigned value;
static int compare(void* aRaw, void* bRaw)
{
char a = static_cast<char>(reinterpret_cast<uintptr_t>(aRaw));
char b = static_cast<char>(reinterpret_cast<uintptr_t>(bRaw));
if (a < b)
return -1;
if (a == b)
return 0;
return 1;
}
static void* getXKey(pas_cartesian_tree_node* node)
{
return reinterpret_cast<void*>(
static_cast<uintptr_t>(reinterpret_cast<TestNode*>(node)->xKey));
}
static void* getYKey(pas_cartesian_tree_node* node)
{
return reinterpret_cast<void*>(
static_cast<uintptr_t>(reinterpret_cast<TestNode*>(node)->yKey));
}
};
static pas_cartesian_tree_config config = {
.get_x_key = TestNode::getXKey,
.get_y_key = TestNode::getYKey,
.x_compare = TestNode::compare,
.y_compare = TestNode::compare
};
static unsigned counter;
struct Pair {
Pair() = default;
Pair(char xKey, char yKey, unsigned value)
: xKey(xKey)
, yKey(yKey)
, value(value)
{
}
bool operator==(const Pair& other) const
{
return xKey == other.xKey;
}
bool operator<(const Pair& other) const
{
return xKey < other.xKey;
}
char xKey;
char yKey;
unsigned value;
};
typedef vector<Pair> PairVector;
PairVector findExact(PairVector& asVector, char xKey)
{
PairVector result;
for (size_t index = 0; index < asVector.size(); ++index) {
if (asVector[index].xKey == xKey)
result.push_back(asVector[index]);
}
sort(result.begin(), result.end());
return result;
}
void remove(PairVector& asVector, size_t index)
{
asVector[index] = asVector.back();
asVector.pop_back();
}
PairVector findLeastGreaterThanOrEqual(PairVector& asVector, char xKey)
{
char bestKey = 0; // assignment to make gcc happy
bool foundKey = false;
for (size_t index = 0; index < asVector.size(); ++index) {
if (asVector[index].xKey >= xKey) {
if (asVector[index].xKey < bestKey || !foundKey) {
foundKey = true;
bestKey = asVector[index].xKey;
}
}
}
PairVector result;
if (!foundKey)
return result;
return findExact(asVector, bestKey);
}
void assertFoundAndRemove(PairVector& asVector, char xKey, char yKey, unsigned value)
{
bool found = false;
size_t foundIndex = 0; // make compilers happy
for (size_t index = 0; index < asVector.size(); ++index) {
if (asVector[index].xKey == xKey
&& asVector[index].yKey == yKey
&& asVector[index].value == value) {
CHECK(!found);
found = true;
foundIndex = index;
}
}
CHECK(found);
remove(asVector, foundIndex);
}
// This deliberately passes a copy of the vector.
void assertEqual(pas_cartesian_tree& asCartesianTree, PairVector asVector)
{
for (TestNode* current = reinterpret_cast<TestNode*>(
pas_cartesian_tree_minimum(&asCartesianTree));
current;
current = reinterpret_cast<TestNode*>(pas_cartesian_tree_node_successor(current)))
assertFoundAndRemove(asVector, current->xKey, current->yKey, current->value);
CHECK(asVector.empty());
}
void assertSameValuesForKey(pas_cartesian_tree& asCartesianTree, TestNode* node,
PairVector foundValues, char xKey)
{
if (node) {
CHECK_EQUAL(node->xKey, xKey);
TestNode* prevNode = node;
do {
node = prevNode;
prevNode = reinterpret_cast<TestNode*>(pas_cartesian_tree_node_predecessor(prevNode));
} while (prevNode && prevNode->xKey == xKey);
if (verbose)
printf("in assertSameValuesForKey, starting the search: node = %p\n", node);
CHECK_EQUAL(node->xKey, xKey);
CHECK(!prevNode || prevNode->xKey < xKey);
do {
if (verbose)
printf("node = %p\n", node);
assertFoundAndRemove(foundValues, node->xKey, node->yKey, node->value);
node = reinterpret_cast<TestNode*>(pas_cartesian_tree_node_successor(node));
CHECK(!node || node->xKey >= xKey);
} while (node && node->xKey == xKey);
}
CHECK(foundValues.empty());
}
static constexpr unsigned nodeDumpWidth = 2;
unsigned cartesianTreePrintWidth(pas_cartesian_tree_node* node)
{
if (!node)
return 0;
return nodeDumpWidth
+ cartesianTreePrintWidth(pas_compact_cartesian_tree_node_ptr_load(&node->left))
+ cartesianTreePrintWidth(pas_compact_cartesian_tree_node_ptr_load(&node->right));
}
void printSpaces(unsigned count)
{
while (count--)
cout << " ";
}
void dumpCartesianTree(pas_cartesian_tree& asCartesianTree, const char* prefix)
{
struct WorkItem {
WorkItem() = default;
explicit WorkItem(pas_cartesian_tree_node* node)
: node(node)
, spaces(0)
{
}
explicit WorkItem(unsigned spaces)
: node(nullptr)
, spaces(spaces)
{
}
pas_cartesian_tree_node* node;
unsigned spaces;
};
vector<WorkItem> workItems;
bool hasRealWork = false;
if (pas_cartesian_tree_node* node = pas_compact_cartesian_tree_node_ptr_load(
&asCartesianTree.root)) {
workItems.push_back(WorkItem(node));
hasRealWork = true;
}
while (hasRealWork) {
vector<WorkItem> currentWorkItems;
swap(workItems, currentWorkItems);
hasRealWork = false;
cout << prefix;
for (WorkItem workItem : currentWorkItems) {
if (workItem.spaces) {
PAS_ASSERT(!workItem.node);
printSpaces(workItem.spaces);
workItems.push_back(workItem);
continue;
}
auto handleChild =
[&] (pas_cartesian_tree_node* child) {
if (!child)
return;
printSpaces(cartesianTreePrintWidth(child));
workItems.push_back(WorkItem(child));
hasRealWork = true;
};
handleChild(pas_compact_cartesian_tree_node_ptr_load(&workItem.node->left));
TestNode* node = static_cast<TestNode*>(workItem.node);
cout << node->xKey;
cout << node->yKey;
workItems.push_back(WorkItem(nodeDumpWidth));
handleChild(pas_compact_cartesian_tree_node_ptr_load(&workItem.node->right));
}
cout << "\n";
}
}
void dumpVector(PairVector vector)
{
bool first = true;
for (const Pair& pair : vector) {
if (first)
first = false;
else
cout << " ";
cout << pair.xKey << pair.yKey;
}
}
// The control string is a null-terminated list of commands. Each
// command is two characters, with the first identifying the operation
// and the second giving a key. The commands are:
// +x Add x
// *x Find all elements equal to x
// @x Find all elements that have the smallest key that is greater than or equal to x
// !x Remove all elements equal to x
void testDriver(const char* controlString)
{
PairVector asVector;
pas_cartesian_tree asCartesianTree;
pas_cartesian_tree_construct(&asCartesianTree);
for (const char* current = controlString; *current;) {
char command = *current++;
char xKey = *current++;
char yKey = 0;
if (command == '+')
yKey = *current++;
unsigned value = ++counter;
PAS_ASSERT(command);
PAS_ASSERT(xKey);
if (command == '+')
PAS_ASSERT(yKey);
if (verbose) {
printf("Running command: %c%c", command, xKey);
if (command == '+')
printf("%c", yKey);
printf(" with current = %p (%zu)\n", current, current - controlString);
}
switch (command) {
case '+': {
pas_heap_lock_lock();
TestNode* node = static_cast<TestNode*>(
pas_utility_heap_allocate(sizeof(TestNode), "TestNode"));
if (verbose)
cout << "Adding node = " << node << "\n";
pas_heap_lock_unlock();
new (node) TestNode(xKey, yKey, value);
pas_cartesian_tree_insert(
&asCartesianTree,
reinterpret_cast<void*>(static_cast<uintptr_t>(xKey)),
reinterpret_cast<void*>(static_cast<uintptr_t>(yKey)),
node,
&config);
asVector.push_back(Pair(xKey, yKey, value));
break;
}
case '*': {
TestNode* node = reinterpret_cast<TestNode*>(
pas_cartesian_tree_find_exact(
&asCartesianTree,
reinterpret_cast<void*>(static_cast<uintptr_t>(xKey)),
&config));
if (verbose)
printf("Found node = %p\n", node);
if (node)
CHECK_EQUAL(node->xKey, xKey);
assertSameValuesForKey(asCartesianTree, node, findExact(asVector, xKey), xKey);
break;
}
case '@': {
TestNode* node = reinterpret_cast<TestNode*>(
pas_cartesian_tree_find_least_greater_than_or_equal(
&asCartesianTree,
reinterpret_cast<void*>(static_cast<uintptr_t>(xKey)),
&config));
if (verbose) {
cout << "CartesianTree:\n";
dumpCartesianTree(asCartesianTree, " ");
cout << "Vector: ";
dumpVector(asVector);
cout << "\n";
}
if (verbose)
printf("Found node = %p\n", node);
if (node) {
CHECK_GREATER_EQUAL(node->xKey, xKey);
assertSameValuesForKey(asCartesianTree, node, findLeastGreaterThanOrEqual(asVector, xKey), node->xKey);
} else
CHECK(findLeastGreaterThanOrEqual(asVector, xKey).empty());
break;
}
case '!': {
while (true) {
TestNode* node = static_cast<TestNode*>(
pas_cartesian_tree_find_exact(
&asCartesianTree,
reinterpret_cast<void*>(static_cast<uintptr_t>(xKey)),
&config));
if (verbose) {
cout << "Found node = " << node;
if (node)
cout <<" (" << node->xKey << node->yKey << ")";
cout << " and removing it.\n";
}
if (node) {
pas_cartesian_tree_remove(&asCartesianTree, node, &config);
CHECK_EQUAL(node->xKey, xKey);
assertFoundAndRemove(asVector, node->xKey, node->yKey, node->value);
if (verbose) {
cout << "Validating cartesian tree right after removal:\n";
dumpCartesianTree(asCartesianTree, " ");
}
pas_cartesian_tree_validate(&asCartesianTree, &config);
} else {
CHECK(findExact(asVector, xKey).empty());
break;
}
}
break;
}
default:
CHECK(!"bad command");
break;
}
if (verbose) {
cout << "Validating cartesian tree:\n";
dumpCartesianTree(asCartesianTree, " ");
}
pas_cartesian_tree_validate(&asCartesianTree, &config);
CHECK_EQUAL(pas_cartesian_tree_size(&asCartesianTree), asVector.size());
assertEqual(asCartesianTree, asVector);
}
}
void randomTestDriver(unsigned numAddOperations, unsigned numOperations)
{
ostringstream out;
const char* things = "abcdefghijklmnopqrstuvwxyzQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
const char* operations = "+*@!";
auto randomChar =
[] (const char* things) -> char {
return things[deterministicRandomNumber(static_cast<unsigned>(strlen(things)))];
};
for (unsigned index = numAddOperations; index--;) {
out << "+" << randomChar(things) << randomChar(things);
}
for (unsigned index = numOperations; index--;) {
char operation = randomChar(operations);
out << operation << randomChar(things);
if (operation == '+')
out << randomChar(things);
}
string str = out.str();
if (verbose)
cout << " Operation string: " << str << "\n";
testDriver(str.c_str());
}
} // anonymous namespace
void addCartesianTreeTests()
{
ADD_TEST(testDriver(""));
ADD_TEST(testDriver("*x@y!z"));
ADD_TEST(testDriver("+aa"));
ADD_TEST(testDriver("+aa*x@y!z"));
ADD_TEST(testDriver("+aa*a@a!a"));
ADD_TEST(testDriver("+aa+ba"));
ADD_TEST(testDriver("+ab+ba"));
ADD_TEST(testDriver("+aa+bb"));
ADD_TEST(testDriver("+ab+bb"));
ADD_TEST(testDriver("+aa+bb+cc"));
ADD_TEST(testDriver("+ab+bc+ca"));
ADD_TEST(testDriver("+ab+ba+cc"));
ADD_TEST(testDriver("+ac+bb+ca"));
ADD_TEST(testDriver("+az+by+cx+dw"));
ADD_TEST(testDriver("+az+by+cx+dw!a"));
ADD_TEST(testDriver("+aw+by+cx+dz"));
ADD_TEST(testDriver("+aw+by+cx+dz!d"));
ADD_TEST(testDriver("+az+by+dx+cw"));
ADD_TEST(testDriver("+az+by+dx+cw!b"));
ADD_TEST(testDriver("+az+bx+dy+cw"));
ADD_TEST(testDriver("+az+bx+dy+cw!c"));
ADD_TEST(testDriver("+az+dy+cx+bw"));
ADD_TEST(testDriver("+az+dy+cx+bw!b"));
ADD_TEST(testDriver("+ay+dz+cx+bw"));
ADD_TEST(testDriver("+ay+dz+cx+bw!d"));
ADD_TEST(testDriver("+az+dy+bx+cw"));
ADD_TEST(testDriver("+az+dy+bx+cw!a"));
ADD_TEST(testDriver("+ax+dy+bz+cw"));
ADD_TEST(testDriver("+ax+dy+bz+cw!c"));
ADD_TEST(testDriver("+dz+by+cx+aw"));
ADD_TEST(testDriver("+dz+by+cx+aw!a"));
ADD_TEST(testDriver("+dz+bw+cx+ay"));
ADD_TEST(testDriver("+dz+bw+cx+ay!b"));
ADD_TEST(testDriver("+dz+by+ax+cw"));
ADD_TEST(testDriver("+dz+by+ax+cw!c"));
ADD_TEST(testDriver("+dz+by+aw+cx"));
ADD_TEST(testDriver("+dz+by+aw+cx!d"));
ADD_TEST(testDriver("+dz+ay+cx+bw"));
ADD_TEST(testDriver("+dz+ay+cx+bw!b"));
ADD_TEST(testDriver("+dw+ax+cy+bz"));
ADD_TEST(testDriver("+dw+ax+cy+bz!c"));
ADD_TEST(testDriver("+dz+ay+bx+cw"));
ADD_TEST(testDriver("+dz+ay+bx+cw!a"));
ADD_TEST(testDriver("+dz+aw+by+cx"));
ADD_TEST(testDriver("+dz+aw+by+cx!d"));
ADD_TEST(testDriver("+ax+by+cz+dx+ay+bz+cx+dy+az+bx+cy+dz+ax+by+cz+dx+ay+bz+cm+dn+ao+bp+cq+dr+am+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq+ar+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq"));
ADD_TEST(testDriver("+ax+by+cz+dx+ay+bz+cx+dy+az+bx+cy+dz+ax+by+cz+dx+ay+bz+cm+dn+ao+bp+cq+dr+am+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq+ar+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq!a!b!c!d"));
ADD_TEST(testDriver("+ax+by+cz+dx+ay+bz+cx+dy+az+bx+cy+dz+ax+by+cz+dx+ay+bz+cm+dn+ao+bp+cq+dr+am+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq+ar+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq!a!b!d!c"));
ADD_TEST(testDriver("+ax+by+cz+dx+ay+bz+cx+dy+az+bx+cy+dz+ax+by+cz+dx+ay+bz+cm+dn+ao+bp+cq+dr+am+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq+ar+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq!a!c!d!b"));
ADD_TEST(testDriver("+ax+by+cz+dx+ay+bz+cx+dy+az+bx+cy+dz+ax+by+cz+dx+ay+bz+cm+dn+ao+bp+cq+dr+am+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq+ar+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq!a!c!b!d"));
ADD_TEST(testDriver("+ax+by+cz+dx+ay+bz+cx+dy+az+bx+cy+dz+ax+by+cz+dx+ay+bz+cm+dn+ao+bp+cq+dr+am+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq+ar+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq!a!d!b!c"));
ADD_TEST(testDriver("+ax+by+cz+dx+ay+bz+cx+dy+az+bx+cy+dz+ax+by+cz+dx+ay+bz+cm+dn+ao+bp+cq+dr+am+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq+ar+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq!a!d!c!b"));
ADD_TEST(testDriver("+ax+by+cz+dx+ay+bz+cx+dy+az+bx+cy+dz+ax+by+cz+dx+ay+bz+cm+dn+ao+bp+cq+dr+am+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq+ar+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq!b!d!c!a"));
ADD_TEST(testDriver("+ax+by+cz+dx+ay+bz+cx+dy+az+bx+cy+dz+ax+by+cz+dx+ay+bz+cm+dn+ao+bp+cq+dr+am+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq+ar+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq!b!d!a!c"));
ADD_TEST(testDriver("+ax+by+cz+dx+ay+bz+cx+dy+az+bx+cy+dz+ax+by+cz+dx+ay+bz+cm+dn+ao+bp+cq+dr+am+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq+ar+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq!d!b!a!c"));
ADD_TEST(testDriver("+ax+by+cz+dx+ay+bz+cx+dy+az+bx+cy+dz+ax+by+cz+dx+ay+bz+cm+dn+ao+bp+cq+dr+am+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq+ar+bn+co+dp+aq+br+cs+dm+an+bo+cp+dq!c!b!a!d"));
ADD_TEST(testDriver("+az+by+cx+dw+av+bu+ct+ds+ar+bq+cp+do+an+bm+cl+dk+aj+bi+ch+dg+af+be+cd+dc+ab+ba+ca+db+ac+bd+ce+df+ag+bh+ci+dj+ak+bl+cm+dn+ax+by+cz+dz+ay+bx+cf+dg+eh+fi+gf+hg+ih+ji+ka+lb+mc+nd+oe+pf+qg+rh+si+tj+uk+vl+xm+yn+zo"));
ADD_TEST(testDriver("+az+by+cx+dw+av+bu+ct+ds+ar+bq+cp+do+an+bm+cl+dk+aj+bi+ch+dg+af+be+cd+dc+ab+ba+ca+db+ac+bd+ce+df+ag+bh+ci+dj+ak+bl+cm+dn+ax+by+cz+dz+ay+bx+cf+dg+eh+fi+gf+hg+ih+ji+ka+lb+mc+nd+oe+pf+qg+rh+si+tj+uk+vl+xm+yn+zo!p!i!a!l!z!m"));
ADD_TEST(testDriver("+az+by+cx+dw+av+bu+ct+ds+ar+bq+cp+do+an+bm+cl+dk+aj+bi+ch+dg+af+be+cd+dc+ab+ba+ca+db+ac+bd+ce+df+ag+bh+ci+dj+ak+bl+cm+dn+ax+by+cz+dz+ay+bx+cf+dg+eh+fi+gf+hg+ih+ji+ka+lb+mc+nd+oe+pf+qg+rh+si+tj+uk+vl+xm+yn+zo!k!i!b!s!r!g"));
ADD_TEST(testDriver("+zg+yh+xq+wi+vn+ud+tp+sy+rf+qg+pq+oh+na+mg+ld+kc+jd+ie+hj+gh+fe+en+ds+cf+br+ad*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!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"));
ADD_TEST(testDriver("+zj+yd+xb+ew+fv+us+ty+sf+rw+qf+pg+oe+nd+mq+ld+kg+jd+iq+hd+ge+fd+ef+dw+cf+bv+aw*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(testDriver("+aj+bf+cb+dr+af+bh+cw+dd+ah+br+cd+dq+ag+bc+cr+df+aq+bh+cr+ds+an+br+cj+dq+aj+bq+cf+dr+af+bv+cc+ds+ab+br+cn+df+aq+bn+cc+dh+aw+bc+ch+de+ar+bb+cq+dn+er+fn+gc+ht+iw+jg+kd+lv+mr+nq+og+pr+qw+rb+st+tj+uu+vn+xd+yv+zt*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!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"));
ADD_TEST(testDriver("+dj+ds+mt+wn@d@m@w@a@g@q"));
ADD_TEST(testDriver("+da+da+da+da+da+da+da+da+da+da+fa+fa+fa+fa+fa+fa+fa+ha+ha+ia+ja+ka+la+ma+oa+pa+qa+ra+za@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"));
ADD_TEST(randomTestDriver(10, 1000));
ADD_TEST(randomTestDriver(20, 1000));
ADD_TEST(randomTestDriver(30, 1000));
ADD_TEST(randomTestDriver(40, 1000));
ADD_TEST(randomTestDriver(50, 1000));
ADD_TEST(randomTestDriver(100, 1000));
ADD_TEST(randomTestDriver(1000, 1000));
ADD_TEST(randomTestDriver(10, 10000));
ADD_TEST(randomTestDriver(20, 10000));
ADD_TEST(randomTestDriver(100, 10000));
ADD_TEST(randomTestDriver(1000, 10000));
}