/* 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 "include/logging.h" #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 del) { //NOBUG_INIT_FLAG (psplay); TRACE (psplay_dbg); REQUIRE (cmp); REQUIRE (key); if (self) { self->tree = NULL; self->found_parent = &self->tree; self->cmp = cmp; self->key = key; self->del = del; self->elem_cnt = 0; self->log2 = 0; } return self; } PSplay psplay_new (psplay_cmp_fn cmp, psplay_key_fn key, psplay_delete_fn del) { PSplay self = malloc (sizeof *self); if (self) { psplay_init (self , cmp, key, del); } return self; } PSplay psplay_destroy (PSplay self) { TRACE (psplay_dbg); if (self) while (self->tree) { PSplaynode n = psplay_remove (self, self->tree); if (self->del) self->del (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_dbg, "%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_dbg, "r is %u", r); if (parent == grandparent->left) { TRACE (psplay_dbg, "ZIG.."); if (node == parent->left) { TRACE (psplay_dbg, "..ZIG"); if (r < PSPLAY_PROB_ZIGZIG) { TRACE (psplay_dbg, "BREAK"); return; } grandparent->left = parent->right; parent->right = grandparent; parent->left = node->right; node->right = parent; } else { TRACE (psplay_dbg, "..ZAG"); if (r < PSPLAY_PROB_ZIGZAG) { TRACE (psplay_dbg, "BREAK"); return; } parent->right = node->left; node->left = parent; grandparent->left = node->right; node->right = grandparent; } } else { TRACE (psplay_dbg, "ZAG.."); if (node == parent->left) { TRACE (psplay_dbg, "..ZIG"); if (r < PSPLAY_PROB_ZIGZAG) { TRACE (psplay_dbg, "BREAK"); return; } parent->left = node->right; node->right = parent; grandparent->right = node->left; node->left = grandparent; } else { TRACE (psplay_dbg, "..ZAG"); if (r < PSPLAY_PROB_ZIGZIG) { TRACE (psplay_dbg, "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_dbg); 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 >= 1UL<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_dbg); 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_dbg); if (!node) return NULL; PSplaynode* r = self->found_parent; while (*r != node) { if (!psplay_find (self, self->key (node), 0)) { WARN (psplay_dbg, "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 < 1UL<log2) --self->log2; return node; } PSplaynode psplay_remove_key (PSplay self, void* key) { return psplay_remove (self, psplay_find (self, key, 0)); } void psplay_delete_node (PSplay self, PSplaynode node) { if (node) self->del (psplay_remove (self, node)); } void psplay_delete_key (PSplay self, void* key) { PSplaynode node = psplay_find (self, key, 0); psplay_delete_node (self, node); } 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->del) self->del (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: */