From bc055ab803455adfdb90b319728770a3657e517f Mon Sep 17 00:00:00 2001 From: Christian Thaeter Date: Tue, 2 Sep 2008 20:28:47 +0200 Subject: [PATCH] Probabilistic Splay Tree implementation Generalized an older implementation I already had, the splay formulas need some improvements. Documentation comes next. --- src/lib/Makefile.am | 2 + src/lib/psplay.c | 505 ++++++++++++++++++++++++++++++++++++ src/lib/psplay.h | 272 +++++++++++++++++++ tests/Makefile.am | 5 + tests/library/test-psplay.c | 471 +++++++++++++++++++++++++++++++++ 5 files changed, 1255 insertions(+) create mode 100644 src/lib/psplay.c create mode 100644 src/lib/psplay.h create mode 100644 tests/library/test-psplay.c diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index ca09d3e25..b372af6b7 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -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 \ diff --git a/src/lib/psplay.c b/src/lib/psplay.c new file mode 100644 index 000000000..6031e3c5f --- /dev/null +++ b/src/lib/psplay.c @@ -0,0 +1,505 @@ +/* + psplay.c - probabilistic splay tree + + Copyright (C) + 2004, 2005, 2006, Christian Thaeter + Copyright (C) CinelerraCV + 2007, 2008, Christian Thaeter + + 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 +#include +#include +#include + +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<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<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: +*/ diff --git a/src/lib/psplay.h b/src/lib/psplay.h new file mode 100644 index 000000000..a2f077c61 --- /dev/null +++ b/src/lib/psplay.h @@ -0,0 +1,272 @@ +/* + psplay.h - probabilistic splay tree + + Copyright (C) + 2004, 2005, 2006, Christian Thaeter + Copyright (C) Lumiera.org + 2007, 2008, Christian Thaeter + + 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 +#include + +/** + * @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: +*/ diff --git a/tests/Makefile.am b/tests/Makefile.am index af3e9f829..1231573d9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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/ diff --git a/tests/library/test-psplay.c b/tests/library/test-psplay.c new file mode 100644 index 000000000..f7989bae7 --- /dev/null +++ b/tests/library/test-psplay.c @@ -0,0 +1,471 @@ +/* + test-psplay.c - test the probanilistic splay tree + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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 +#include + +#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: +*/