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:
parent
9471e47cdf
commit
bc055ab803
5 changed files with 1255 additions and 0 deletions
|
|
@ -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
505
src/lib/psplay.c
Normal 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
272
src/lib/psplay.h
Normal 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:
|
||||
*/
|
||||
|
|
@ -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
471
tests/library/test-psplay.c
Normal 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:
|
||||
*/
|
||||
Loading…
Reference in a new issue