Probabilistic Splay Tree implementation

Generalized an older implementation I already had, the splay formulas need
some improvements. Documentation comes next.
This commit is contained in:
Christian Thaeter 2008-09-02 20:28:47 +02:00
parent 9471e47cdf
commit bc055ab803
5 changed files with 1255 additions and 0 deletions

View file

@ -30,6 +30,7 @@ liblumi_a_SOURCES = \
$(liblumi_a_srcdir)/luid.c \
$(liblumi_a_srcdir)/safeclib.c \
$(liblumi_a_srcdir)/cuckoo.c \
$(liblumi_a_srcdir)/psplay.c \
$(liblumi_a_srcdir)/mrucache.c \
$(liblumi_a_srcdir)/time.c \
$(liblumi_a_srcdir)/appconfig.cpp
@ -43,6 +44,7 @@ noinst_HEADERS += \
$(liblumi_a_srcdir)/luid.h \
$(liblumi_a_srcdir)/safeclib.h \
$(liblumi_a_srcdir)/cuckoo.h \
$(liblumi_a_srcdir)/psplay.h \
$(liblumi_a_srcdir)/mrucache.h \
$(liblumi_a_srcdir)/time.h \
$(liblumi_a_srcdir)/appconfig.hpp \

505
src/lib/psplay.c Normal file
View file

@ -0,0 +1,505 @@
/*
psplay.c - probabilistic splay tree
Copyright (C)
2004, 2005, 2006, Christian Thaeter <chth@gmx.net>
Copyright (C) CinelerraCV
2007, 2008, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "lib/psplay.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <nobug.h>
NOBUG_DEFINE_FLAG (psplay);
#ifndef PSPLAY_TRAIL_DEPTH
#define PSPLAY_TRAIL_DEPTH 128
#endif
/*
probabilistic distribution, this are the direct splay equations used to determine if to splay
or break out of the splaying algorithm.
useable variables/functions are:
self->log2 - log2 of the tree elements, this would be the depth of a fully balanced tree
splayfactor - user defined weigth for splaying, we define '100' to be the default
depth - depth of the current node in the tree
trail->dir - dervitation from tree center
psplay_fast_prng () - returns a prng in the range 1...2^31
*/
#define PSPLAY_FORMULA (self->log2*100/(depth + (psplay_fast_prng () & 63)) + trail->dir) * splayfactor
#ifndef PSPLAY_PROB_ZIGZIG
#define PSPLAY_PROB_ZIGZIG 5000
#endif
#ifndef PSPLAY_PROB_ZIGZAG
#define PSPLAY_PROB_ZIGZAG 2500
#endif
/* simple prng with 2^31-1 cycle */
static inline uint32_t psplay_fast_prng ()
{
static uint32_t rnd=0xbabeface;
return rnd = rnd<<1 ^ ((rnd >> 30) & 1) ^ ((rnd>>2) & 1);
}
PSplay
psplay_init (PSplay self, psplay_cmp_fn cmp, psplay_key_fn key, psplay_delete_fn delete)
{
NOBUG_INIT_FLAG (psplay);
TRACE (psplay);
REQUIRE (cmp);
REQUIRE (key);
if (self)
{
self->tree = NULL;
self->found_parent = &self->tree;
self->cmp = cmp;
self->key = key;
self->delete = delete;
self->elem_cnt = 0;
self->log2 = 0;
}
return self;
}
PSplay
psplay_new (psplay_cmp_fn cmp, psplay_key_fn key, psplay_delete_fn delete)
{
PSplay self = malloc (sizeof *self);
if (self)
{
psplay_init (self , cmp, key, delete);
}
return self;
}
PSplay
psplay_destroy (PSplay self)
{
TRACE (psplay);
if (self) while (self->tree)
{
PSplaynode n = psplay_remove (self, self->tree);
if (self->delete)
self->delete (n);
}
return self;
}
void
psplay_delete (PSplay self)
{
free (psplay_destroy (self));
}
PSplaynode
psplaynode_init (PSplaynode self)
{
if (self)
self->left = self->right = NULL;
return self;
}
/*
Lookup operations (lookup and insert) record the path as they decend into the tree
this will allow bottom up splaying without storing 'up' pointers in the node.
The length of this trail (PSPLAY_TRAIL_DEPTH) also define the maximal limit on how much
a node can be splayed up giving some hard bound for the splay operation.
General wisdom tells that top down splaying is more efficent to implement than bottom
up splaying. Nevertheless we do bottom up splaying here because we can decide
randomly on each level if we want to continue splaying or not. No splaying
is certainly more efficent than top-down splaying.
*/
struct psplaytrail
{
int dir;
unsigned depth;
PSplaynode* trail[PSPLAY_TRAIL_DEPTH];
};
static inline unsigned
trailidx (unsigned n)
{
return n & (PSPLAY_TRAIL_DEPTH-1);
}
static inline void
psplay_splay (PSplay self, struct psplaytrail* trail, unsigned splayfactor)
{
TRACE (psplay, "%p %u", self, splayfactor);
if (trail->dir < 0) trail->dir = - trail->dir;
for (unsigned lim = PSPLAY_TRAIL_DEPTH, depth = trail->depth; lim > 2 && depth > 2; lim-=2, depth-=2)
{
PSplaynode node = *trail->trail [trailidx (depth)];
PSplaynode parent = *trail->trail [trailidx (depth-1)];
PSplaynode grandparent = *trail->trail [trailidx (depth-2)];
unsigned r = PSPLAY_FORMULA;
TRACE (psplay, "r is %u", r);
if (parent == grandparent->left)
{
TRACE (psplay, "ZIG..");
if (node == parent->left)
{
TRACE (psplay, "..ZIG");
if (r < PSPLAY_PROB_ZIGZIG)
{
TRACE (psplay, "BREAK");
return;
}
grandparent->left = parent->right;
parent->right = grandparent;
parent->left = node->right;
node->right = parent;
}
else
{
TRACE (psplay, "..ZAG");
if (r < PSPLAY_PROB_ZIGZAG)
{
TRACE (psplay, "BREAK");
return;
}
parent->right = node->left;
node->left = parent;
grandparent->left = node->right;
node->right = grandparent;
}
}
else
{
TRACE (psplay, "ZAG..");
if (node == parent->left)
{
TRACE (psplay, "..ZIG");
if (r < PSPLAY_PROB_ZIGZAG)
{
TRACE (psplay, "BREAK");
return;
}
parent->left = node->right;
node->right = parent;
grandparent->right = node->left;
node->left = grandparent;
}
else
{
TRACE (psplay, "..ZAG");
if (r < PSPLAY_PROB_ZIGZIG)
{
TRACE (psplay, "BREAK");
return;
}
grandparent->right = parent->left;
parent->left = grandparent;
parent->right = node->left;
node->left = parent;
}
}
*trail->trail [trailidx (depth-2)] = node;
}
}
PSplaynode
psplay_insert (PSplay self, PSplaynode node, int splayfactor)
{
TRACE (psplay);
PSplaynode n = self->tree;
struct psplaytrail trail;
trail.dir = 0;
trail.depth = 0;
trail.trail [0] = &self->tree;
if (!n)
self->tree = node;
else
{
while (n != node)
{
int c;
c = self->cmp (self->key (node), self->key (n));
++trail.depth;
if (c < 0)
{
--trail.dir;
if (!n->left)
n->left = node;
trail.trail [trailidx (trail.depth)] = &n->left;
n = n->left;
}
else if (c > 0)
{
++trail.dir;
if (!n->right)
n->right = node;
trail.trail [trailidx (trail.depth)] = &n->right;
n = n->right;
}
else
{
TODO ("policy for multiple entered items (before, after, fail, replace)");
return NULL;
}
}
}
++self->elem_cnt;
if (self->elem_cnt >= 1<<self->log2) ++self->log2;
if (splayfactor && trail.depth > 2)
psplay_splay (self, &trail, splayfactor);
return node;
}
PSplaynode
psplay_find (PSplay self, const void* key, int splayfactor)
{
TRACE (psplay);
PSplaynode node = self->tree;
struct psplaytrail trail;
trail.dir = 0;
trail.depth = 0;
trail.trail [0] = &self->tree;
while (node)
{
int c;
c = self->cmp (key, self->key (node));
++trail.depth;
if (c < 0)
{
--trail.dir;
trail.trail [trailidx (trail.depth)] = &node->left;
node = node->left;
}
else if (c > 0)
{
++trail.dir;
trail.trail [trailidx (trail.depth)] = &node->right;
node = node->right;
}
else
{
self->found_parent = trail.trail [trailidx (--trail.depth)];
break;
}
}
if (node && splayfactor && trail.depth > 2)
psplay_splay (self, &trail, splayfactor);
return node;
}
PSplaynode
psplay_remove (PSplay self, PSplaynode node)
{
TRACE (psplay);
if (!node) return NULL;
PSplaynode* r = self->found_parent;
while (*r != node)
{
if (!psplay_find (self, self->key (node), 0))
{
WARN (psplay, "node %p is not in splay tree %p", node, self);
return NULL;
}
r = self->found_parent;
}
if (!node->left)
*r = node->right;
else if (!node->right)
*r = node->left;
else
{
PSplaynode i, iparent = NULL;
if (psplay_fast_prng()&1) /* 50% probability removing left or right wards */
{
for (i = node->left; i->right; iparent = i, i = i->right);
if (iparent)
iparent->right = i->left;
if (node->left != i)
i->left = node->left;
i->right = node->right;
}
else
{
for (i = node->right; i->left; iparent = i, i = i->left);
if (iparent)
iparent->left = i->right;
if (node->right != i)
i->right = node->right;
i->left = node->left;
}
*r = i;
}
--self->elem_cnt;
if (self->elem_cnt < 1<<self->log2) --self->log2;
return node;
}
PSplaynode
psplay_remove_key (PSplay self, void* key)
{
return psplay_remove (self, psplay_find (self, key, 0));
}
const psplay_delete_fn PSPLAY_CONT = (psplay_delete_fn)0x0;
const psplay_delete_fn PSPLAY_STOP = (psplay_delete_fn)0x1;
const psplay_delete_fn PSPLAY_REMOVE = (psplay_delete_fn)0x2;
static int
psplay_handle (PSplay self, PSplaynode node, psplay_delete_fn res)
{
if (res == PSPLAY_CONT)
return 1;
if (res == PSPLAY_STOP)
;
else if (res == PSPLAY_REMOVE)
{
psplay_remove (self, node);
if (self->delete)
self->delete (node);
}
else
{
psplay_remove (self, node);
res (node);
}
return 0;
}
int
psplay_walk (PSplay self, PSplaynode node, psplay_action_fn action, int level, void* data)
{
if (!self->tree)
return 1;
if (!node)
node = self->tree;
psplay_delete_fn res;
res = action (node, PSPLAY_PREORDER, level, data);
if (!psplay_handle (self, node, res))
return 0;
if (node->left)
if (!psplay_walk (self, node->left, action, level+1, data))
return 0;
res = action (node, PSPLAY_INORDER, level, data);
if (!psplay_handle (self, node, res))
return 0;
if (node->right)
if (!psplay_walk (self, node->right, action, level+1, data))
return 0;
res = action (node, PSPLAY_POSTORDER, level, data);
if (!psplay_handle (self, node, res))
return 0;
return 1;
}
static psplay_delete_fn
psplay_print_node (PSplaynode node, const enum psplay_order_enum which, int level, void* data)
{
FILE* fh = data;
static char* sp = " ";
if (level>40)
{
if (which == PSPLAY_PREORDER)
fprintf (fh, "%s ...\n", sp);
return PSPLAY_CONT;
}
switch (which)
{
case PSPLAY_PREORDER:
fprintf (fh, "%s%p\n", sp+40-level, node);
if (node->left)
fprintf (fh, "%sleft %p\n", sp+40-level, node->left);
break;
case PSPLAY_INORDER:
if (node->right)
fprintf (fh, "%sright %p\n", sp+40-level, node->right);
break;
case PSPLAY_POSTORDER:
break;
}
return PSPLAY_CONT;
}
void
psplay_dump (PSplay self, FILE* dest)
{
fprintf (dest, "root %p\n", self->tree);
psplay_walk (self, NULL, psplay_print_node, 0, dest);
}
/*
// Local Variables:
// mode: C
// c-file-style: "gnu"
// indent-tabs-mode: nil
// End:
*/

272
src/lib/psplay.h Normal file
View file

@ -0,0 +1,272 @@
/*
psplay.h - probabilistic splay tree
Copyright (C)
2004, 2005, 2006, Christian Thaeter <chth@gmx.net>
Copyright (C) Lumiera.org
2007, 2008, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef PSPLAY_H
#define PSPLAY_H
#include <stdint.h>
#include <stdio.h>
/**
* @file
* Probabilistic splay trees
* A splay trees is self-optimizing (in contrast to self-balancing) datastructure.
* We introduce here a probabilistic bottom up approach which reduces the splay costs.
* Without affecting the performance. The randomization gives also some insurance that
* worst case situations are extremely unlikely.
* Tree nodes are very small (just 2 pointers) and are intrusively placed into the users
* datastructure.
*/
/**
* Type and handle for a psplay tree node
* This node have to be placed inside users data.
*/
typedef struct psplaynode_struct psplaynode;
typedef psplaynode* PSplaynode;
struct psplaynode_struct
{
PSplaynode left;
PSplaynode right;
};
/**
* Function use to compare keys
* @param a first key
* @param b second key
* @return shall return -1/0/1 when a is less than/equal to/biggier than b.
*/
typedef int (*psplay_cmp_fn)(const void* a, const void* b);
/**
* Destructor for user defined data
* Called when an element got removed from a splay tree.
* @param node pointer to the intrusive node inside the users datastructure
* The user is responsible for casting 'node' back to his real datastructure (maybe with OFFSET_OF()),
* free all resources associated with it and finally free the datastructure itself.
*/
typedef void (*psplay_delete_fn)(PSplaynode node);
/**
* Retrieve the key from a user datastructure
* @param node pointer to the intrusive node inside the users datastructure
* This functiom must return a pointer to the key under which the user stores his data.
*/
typedef const void* (*psplay_key_fn)(const PSplaynode node);
/**
* Type and handle for a psplay root structure
* This structure shall be treated opaque, its only defined in the header to allow
* one to integrate it directly instead referencing it.
*/
typedef struct psplay_struct psplay;
typedef psplay* PSplay;
struct psplay_struct
{
PSplaynode tree; /* the tree */
PSplaynode* found_parent; /* maybe direct parent of last found node, used for fast remove */
psplay_cmp_fn cmp;
psplay_key_fn key;
psplay_delete_fn delete;
size_t elem_cnt;
unsigned log2; /* roughly log2 of the elem_cnt*/
};
/**
* Number of elements in tree
* @param self pointer to the tree
* @return number of elements
*/
static inline size_t
psplay_nelements (PSplay self)
{
return self->elem_cnt;
};
/**
* Initialize a splay tree
* @param self pointer to the psplay structure
* @param cmp user supplied compare function
* @param key user supplied function to retrieve a key
* @param delete user supplied destructor function or NULL if no destructor is necessary
* @return self
*/
PSplay
psplay_init (PSplay self, psplay_cmp_fn cmp, psplay_key_fn key, psplay_delete_fn delete);
/**
* Destroy a splay tree
* Frees all elements and associated resources of a splay tree
* @param self pointer to the psplay structure
* @return self
*/
PSplay
psplay_destroy (PSplay self);
/**
* Allocate a splay tree
* @param cmp user supplied compare function
* @param key user supplied function to retrieve a key
* @param delete user supplied destructor function or NULL if no destructor is necessary
* @return allcoated splay tree or NULL on error
*/
PSplay
psplay_new (psplay_cmp_fn cmp, psplay_key_fn key, psplay_delete_fn delete);
/**
* Delete a splay tree
* Frees all elements and associated resources of a splay tree and then itseld
* @param self pointer to the psplay structure
*/
void
psplay_delete (PSplay self);
/**
* Initialize a splay tree node
* The user has to place this nodes within his datastructure and must
* initialize them before using them.
* @param self pointer to the node to be initialized
* @return self
*/
PSplaynode
psplaynode_init (PSplaynode self);
/**
* Insert a element into a splay tree
* @param self pointer to the splay tree
* @param node pointer to the node to be inserted
* @param splayfactor weight for the probabilistic splaying,
* 0 disables the splaying, 100 is the expected normal value
* use 100 when in doubt
* @return self or NULL when a node with same key already in the tree
*/
PSplaynode
psplay_insert (PSplay self, PSplaynode node, int splayfactor);
/**
* Find a element in a splay tree
* @param self pointer to the splay tree
* @param key pointer to the key to be searched
* @param splayfactor weight for the probabilistic splaying,
* 0 disables the splaying, 100 is the expected normal value
* @return found node or NULL if the key was not found in the tree
*/
PSplaynode
psplay_find (PSplay self, const void* key, int splayfactor);
/**
* Remove a node from a splay tree
* @param self pointer to the splay tree
* @param node node to be removed
* @return pointer to the removed node
* removal is optimized for the case where one call it immediately after one did a
* psplay_find() as last operation on that tree
*/
PSplaynode
psplay_remove (PSplay self, PSplaynode node);
/**
* Remove a node by key from a splay tree
* @param self pointer to the splay tree
* @param key key of the node to be removed
* @return pointer to the removed node
*/
PSplaynode
psplay_remove_key (PSplay self, void* key);
enum psplay_order_enum
{
PSPLAY_PREORDER,
PSPLAY_INORDER,
PSPLAY_POSTORDER
};
/**
* Traverse a splay tree
* Traversing a tree calls a user supplied action three times
* An 'action' must not alter the tree itself but it can indicate aborting the tree traversal and
* how the current node is handled by its return value.
* @param node pointer to the currently traversed node
* @param which state of the traversal:
* PSPLAY_PREORDER before visiting the left subtree,
* PSPLAY_INORDER after visiting the left subtree and before the right subtree
* PSPLAY_POSTORDER finally after visiting the right subtree.
* Example: For to traverse the tree in order action would only handle PSPLAY_INORDER.
* This action shall return PSPLAY_CONT when the traversal of the tree shall continue.
* @param level depth of the node in the tree
* @param data user supplied data which is transparently passed around
* @return a pointer to a function which indicates how to proceed, there are three special
* return values predefined:
* PSPLAY_CONT - continue with the traversal
* PSPLAY_STOP - stop the traversal
* PSPLAY_REMOVE - stops the traversal and removes the current node, calling the delete handler
* any other psplay_delete_fn - stops the traversal and removes the current node, calling the returned delete handler with it
*/
typedef psplay_delete_fn (*psplay_action_fn)(PSplaynode node, const enum psplay_order_enum which, int level, void* data);
extern const psplay_delete_fn PSPLAY_CONT;
extern const psplay_delete_fn PSPLAY_STOP;
extern const psplay_delete_fn PSPLAY_REMOVE;
/**
* Start a tree traversal
* @param self the tree to be traversed
* @param node pointer to root node where traversal shall start, use NULL for the whole tree
* @param action handler function as defined above
* @param level initial value for the level
* @param data user supplied data which is transparently passed to the action
* @return 0 when the tree traversal got aborted (by anything but PSPLAY_CONT as action handler return)
* 1 when the whole tree was traversed successfully
*/
int
psplay_walk (PSplay self, PSplaynode node, psplay_action_fn action, int level, void* data);
void
psplay_dump (PSplay self, FILE* dest);
#endif
/*
// Local Variables:
// mode: C
// c-file-style: "gnu"
// indent-tabs-mode: nil
// End:
*/

View file

@ -33,6 +33,11 @@ test_llist_SOURCES = $(tests_srcdir)/library/test-llist.c
test_llist_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/
test_llist_LDADD = liblumi.a -lnobugmt -lpthread -ldl -lm
check_PROGRAMS += test-psplay
test_psplay_SOURCES = $(tests_srcdir)/library/test-psplay.c
test_psplay_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/
test_psplay_LDADD = liblumi.a -lnobugmt -lpthread -ldl -lm
check_PROGRAMS += test-safeclib
test_safeclib_SOURCES = $(tests_srcdir)/library/test-safeclib.c
test_safeclib_CPPFLAGS = $(AM_CPPFLAGS) -std=gnu99 -Wall -Werror -I$(top_srcdir)/src/

471
tests/library/test-psplay.c Normal file
View file

@ -0,0 +1,471 @@
/*
test-psplay.c - test the probanilistic splay tree
Copyright (C) Lumiera.org
2008, Christian Thaeter <ct@pipapo.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <time.h>
#include <stdlib.h>
#include "lib/psplay.h"
#include "tests/test.h"
struct testitem
{
psplaynode node;
char* key;
};
typedef struct testitem* TestItem;
TestItem
testitem_new (const char* str)
{
TestItem self = malloc (sizeof *self);
psplaynode_init (&self->node);
self->key = strdup (str);
return self;
}
void
testitem_delete (TestItem self)
{
free (self->key);
free (self);
}
static int
cmp_fn (const void*, const void*);
static const void*
key_fn (const PSplaynode);
static void
delete_fn (PSplaynode);
//static psplay_delete_fn
//action_fn (PSplaynode node, const enum psplay_order_e which, int level, void* data);
static int
fcmp_fn (const void*, const void*);
static const void*
fkey_fn (const PSplaynode);
static void
fdelete_fn (PSplaynode);
//static psplay_delete_fn
//action_fn (PSplaynode node, const enum psplay_order_e which, int level, void* data);
psplay_delete_fn
testitem_print_node (PSplaynode node, const enum psplay_order_enum which, int level, void* data)
{
FILE* fh = data;
static char* sp = " ";
if (level>40)
{
if (which == PSPLAY_PREORDER)
fprintf (fh, "%s ...\n", sp);
return PSPLAY_CONT;
}
switch (which)
{
case PSPLAY_PREORDER:
fprintf (fh, "%s%p '%s'\n", sp+40-level, node, ((TestItem)node)->key);
if (node->left) fprintf (fh, "%sleft %p '%s'\n", sp+40-level, node->left, ((TestItem)node->left)->key);
break;
case PSPLAY_INORDER:
if (node->right) fprintf (fh, "%sright %p '%s'\n", sp+40-level, node->right, ((TestItem)node->right)->key);
break;
case PSPLAY_POSTORDER:
break;
}
return PSPLAY_CONT;
}
void
testitem_dump (PSplay self, FILE* dest)
{
fprintf (dest, "root %p '%s'\n", self->tree, self->tree?((TestItem)self->tree)->key:"EMPTY");
psplay_walk (self, NULL, testitem_print_node, 0, dest);
fprintf (dest, "\n");
}
psplay_delete_fn
testitem_graphvizprint_node (PSplaynode node, const enum psplay_order_enum which, int level, void* data)
{
FILE* fh = data;
switch (which)
{
case PSPLAY_PREORDER:
if (node->left)
fprintf (fh, "\t\"%p:%s\":sw -> \"%p:%s\":ne;\n",
node,
((TestItem)node)->key,
node->left,
((TestItem)node->left)->key);
break;
case PSPLAY_INORDER:
if (node->right)
fprintf (fh, "\t\"%p:%s\":se -> \"%p:%s\":nw;\n",
node,
((TestItem)node)->key,
node->right,
((TestItem)node->right)->key);
break;
case PSPLAY_POSTORDER:
break;
}
return PSPLAY_CONT;
}
void
testitem_graphvizdump (PSplay self, FILE* dest)
{
static int cnt = 0;
if (!cnt) cnt = (time(NULL) % 1000) * 100;
char cmd[256];
sprintf(cmd,"dot -Tps >/var/tmp/dbg%d.ps; gv /var/tmp/dbg%d.ps",cnt,cnt);
FILE * graph = popen(cmd, "w");
fprintf(graph, "digraph \"%s\" { center=true; size=\"6,6\"; node [color=lightblue2, style=filled];", "psplay");
++cnt;
fprintf(graph, "\t\"root\":s -> \"%p:%s\":n;\n",
self->tree, self->tree?((TestItem)self->tree)->key:"EMPTY");
psplay_walk (self, NULL, testitem_graphvizprint_node, 0, graph);
fprintf(graph, "}");
pclose(graph);
}
TESTS_BEGIN
TEST ("basic")
{
psplay splay_tree;
psplay_init (&splay_tree, cmp_fn, key_fn, delete_fn);
psplay_dump (&splay_tree, stdout);
psplay_destroy (&splay_tree);
}
TEST ("basic_insert_dump")
{
REQUIRE (argv[2]);
psplay splay_tree;
psplay_init (&splay_tree, cmp_fn, key_fn, delete_fn);
int end = atoi (argv[2]);
char key[1024];
for (int i = 1; i <= end; ++i)
{
sprintf (key, "%d", i);
TRACE (tests, "insert %s", key);
psplay_insert (&splay_tree, (PSplaynode)testitem_new (key), 100);
}
psplay_dump (&splay_tree, stderr);
#if 0
for (int i = 1; i <= end; ++i)
{
sprintf (key, "%d", i);
TRACE (tests, "insert %s", key);
psplay_remove_key (&splay_tree, key);
psplay_dump (&splay_tree, stderr);
}
for (int i = end; i; --i)
{
sprintf (key, "%d", i);
TRACE (tests, "insert %s", key);
psplay_remove_key (&splay_tree, key);
psplay_dump (&splay_tree, stderr);
}
#endif
psplay_destroy (&splay_tree);
printf ("done\n");
}
TEST ("insert_find")
{
psplay splay_tree;
psplay_init (&splay_tree, cmp_fn, key_fn, delete_fn);
psplay_insert (&splay_tree, (PSplaynode)testitem_new ("foo"), 100);
psplay_insert (&splay_tree, (PSplaynode)testitem_new ("bar"), 100);
psplay_insert (&splay_tree, (PSplaynode)testitem_new ("baz"), 100);
psplay_insert (&splay_tree, (PSplaynode)testitem_new ("test"), 100);
psplay_insert (&splay_tree, (PSplaynode)testitem_new ("pap"), 100);
psplay_insert (&splay_tree, (PSplaynode)testitem_new ("qux"), 100);
testitem_graphvizdump (&splay_tree, stdout);
psplay_dump (&splay_tree, stdout);
//TestItem f = (TestItem) psplay_find (&splay_tree, "baz", 100);
TestItem f = (TestItem) psplay_find (&splay_tree, "baz", 100);
ENSURE (f);
printf ("found %p (%.4s)\n", &f->node, f->key);
psplay_dump (&splay_tree, stdout);
f = (TestItem) psplay_find (&splay_tree, "test", 100);
ENSURE (f);
printf ("found %p (%.4s)\n", &f->node, f->key);
psplay_dump (&splay_tree, stdout);
f = (TestItem) psplay_find (&splay_tree, "test", 100);
ENSURE (f);
printf ("found %p (%.4s)\n", &f->node, f->key);
psplay_dump (&splay_tree, stdout);
f = (TestItem) psplay_find (&splay_tree, "foo", 100);
ENSURE (f);
printf ("found %p (%.4s)\n", &f->node, f->key);
psplay_dump (&splay_tree, stdout);
#if 0
psplay_delete (psplay_remove (&splay_tree, root.tree));
psplay_dump (&splay_tree, stdout);
psplay_delete (psplay_remove (&splay_tree, root.tree));
psplay_dump (&splay_tree, stdout);
psplay_delete (psplay_remove (&splay_tree, root.tree));
psplay_dump (&splay_tree, stdout);
#endif
printf ("destroying\n");
psplay_destroy (&splay_tree);
psplay_dump (&splay_tree, stdout);
#if 0
psplay_delete (psplay_remove (&splay_tree, root.tree));
psplay_dump (&splay_tree, stdout);
psplay_delete (psplay_remove (&splay_tree, root.tree));
psplay_dump (&splay_tree, stdout);
psplay_delete (psplay_remove (&splay_tree, root.tree));
psplay_dump (&splay_tree, stdout);
#endif
return 0;
}
TEST ("basic_insert_splay")
{
REQUIRE (argv[2]);
psplay splay_tree;
psplay_init (&splay_tree, cmp_fn, key_fn, delete_fn);
int end = atoi (argv[2]);
char key[1024];
for (int i = 1; i <= end; ++i)
{
sprintf (key, "%d", i);
TRACE (tests, "insert %s", key);
psplay_insert (&splay_tree, (PSplaynode)testitem_new (key), 100);
}
for (int i = end/2; i <= end; ++i)
{
psplay_dump (&splay_tree, stderr);
sprintf (key, "%d", i);
psplay_find (&splay_tree, key, 100);
}
psplay_destroy (&splay_tree);
printf ("done\n");
}
TEST ("basic_rand_insert_dump")
{
REQUIRE (argv[2]);
psplay splay_tree;
psplay_init (&splay_tree, cmp_fn, key_fn, delete_fn);
int end = atoi (argv[2]);
char key[1024];
for (int i = 1; i <= end; ++i)
{
sprintf (key, "%d", i /*rand()*/);
psplay_insert (&splay_tree, (PSplaynode)testitem_new (key), 100);
}
testitem_graphvizdump (&splay_tree, stdout);
//testitem_dump (&splay_tree, stdout);
psplay_destroy (&splay_tree);
printf ("done\n");
}
TEST ("fast_insert")
{
REQUIRE (argv[2]);
psplay splay_tree;
psplay_init (&splay_tree, fcmp_fn, fkey_fn, fdelete_fn);
int end = atoi (argv[2]);
char key[1024];
for (int i = 1; i <= end; ++i)
{
sprintf (key, "%d", i);
psplay_insert (&splay_tree, (PSplaynode)testitem_new (key), 100);
}
psplay_destroy (&splay_tree);
printf ("done\n");
}
TEST ("nonexistant")
{
REQUIRE (argv[2]);
}
TEST ("insert")
{
REQUIRE (argv[2]);
}
TEST ("insert_rand")
{
REQUIRE (argv[2]);
}
TEST ("insert_fastcheck")
{
REQUIRE (argv[2]);
}
TESTS_END
/*
cuckoo support functions
*/
static int
cmp_fn (const void* a, const void* b)
{
REQUIRE (a);
REQUIRE (b);
return strcmp (a, b);
}
static const void*
key_fn (const PSplaynode node)
{
REQUIRE (node);
REQUIRE (((TestItem)node)->key);
return ((TestItem)node)->key;
}
static void
delete_fn (PSplaynode node)
{
REQUIRE (node);
testitem_delete ((TestItem) node);
}
//static psplay_delete_fn
//action_fn (PSplaynode node, const enum psplay_order_e which, int level, void* data)
//{
//}
static int
fcmp_fn (const void* a, const void* b)
{
return strcmp (a, b);
}
static const void*
fkey_fn (const PSplaynode node)
{
return ((TestItem)node)->key;
}
static void
fdelete_fn (PSplaynode node)
{
testitem_delete ((TestItem) node);
}
//static psplay_delete_fn
//action_fn (PSplaynode node, const enum psplay_order_e which, int level, void* data)
//{
//}
/*
// Local Variables:
// mode: C
// c-file-style: "gnu"
// indent-tabs-mode: nil
// End:
*/