]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Red Black Tree: A structure to keep data sorted
authorJan Maria Matejka <mq@ucw.cz>
Mon, 3 Dec 2018 09:06:59 +0000 (10:06 +0100)
committerJan Maria Matejka <mq@ucw.cz>
Mon, 3 Dec 2018 09:06:59 +0000 (10:06 +0100)
This is a quite straightforward implementation of RBT together with
a unit test. It is assumed that all the keys are unique.

Capabilities: find, insert, delete, first, next.

lib/Makefile
lib/redblack.h [new file with mode: 0644]
lib/redblack_test.c [new file with mode: 0644]

index 01f3114d65b8bcf23c9a702bbabfc67e656a0c4c..e5ca0d7635115b9a6a33a5b9b8486c653a7069f5 100644 (file)
@@ -2,6 +2,6 @@ src := bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mem
 obj := $(src-o-files)
 $(all-daemon)
 
-tests_src := heap_test.c buffer_test.c event_test.c flowspec_test.c bitops_test.c patmatch_test.c fletcher16_test.c slist_test.c checksum_test.c lists_test.c mac_test.c ip_test.c hash_test.c printf_test.c
+tests_src := heap_test.c buffer_test.c event_test.c flowspec_test.c bitops_test.c patmatch_test.c fletcher16_test.c slist_test.c checksum_test.c lists_test.c mac_test.c ip_test.c hash_test.c printf_test.c redblack_test.c
 tests_targets := $(tests_targets) $(tests-target-files)
 tests_objs := $(tests_objs) $(src-o-files)
diff --git a/lib/redblack.h b/lib/redblack.h
new file mode 100644 (file)
index 0000000..2c44db2
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ *     BIRD Internet Routing Daemon -- Red Black Tree
+ *
+ *     (c) 2018 Maria Matejka <mq@jmq.cz>
+ *     (c) 2018 CZ.NIC z.s.p.o.
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_REDBLACK_H_
+#define _BIRD_REDBLACK_H_
+
+/* Assumption: The nodes are always residing on an even address.
+ * We store the node blackness into the parent pointer LSB. */
+
+/*
+ * Typical use case:
+ * struct mystruct {
+ *   whatever;
+ *   REDBLACK_NODE(struct mystruct, myprefix_);
+ *   whatever;
+ * };
+ */
+
+#define REDBLACK_NODE(type, name) type *name[3]
+
+/* Color bit definition */
+#define REDBLACK_BLACK 0
+#define REDBLACK_RED   1
+
+/* Parent pointer and color composition and resolution */
+#define REDBLACK_PARENT_POINTER(name, what) (what)->name[0]
+#define REDBLACK_PTR(type, pointer) ((type *) pointer)
+#define REDBLACK_PTR_RED(type, pointer) REDBLACK_PTR(type, (((uintptr_t) (pointer)) | 1))
+#define REDBLACK_PTR_BLACK(type, pointer) (pointer)
+#define REDBLACK_PTR_COLOR(pointer) (((uintptr_t) (pointer)) & 1)
+#define REDBLACK_NODE_COLOR(name, what) REDBLACK_PTR_COLOR(REDBLACK_PARENT_POINTER(name, what))
+#define REDBLACK_PTR_COMPOSE(type, pointer, color) REDBLACK_PTR(type, (((uintptr_t) (pointer)) | (color)))
+#define REDBLACK_PTR_PTR(type, pointer) REDBLACK_PTR(type, (((uintptr_t) (pointer)) & ~1))
+#define REDBLACK_PARENT(type, name, what) REDBLACK_PTR_PTR(type, REDBLACK_PARENT_POINTER(name, what))
+#define REDBLACK_SET_COLOR(type, name, what, color) \
+  (REDBLACK_PARENT_POINTER(name, what) = REDBLACK_PTR_COMPOSE(type, REDBLACK_PARENT(type, name, what), color))
+
+/* Left and right direction */
+#define REDBLACK_LEFT  1
+#define REDBLACK_RIGHT 2
+#define REDBLACK_CHILD(name, what, where) (what)->name[where]
+#define REDBLACK_CHILDREN(name, what) { (what)->name[REDBLACK_LEFT], (what)->name[REDBLACK_RIGHT] }
+#define REDBLACK_LEFT_CHILD(name, what)  REDBLACK_CHILD(name, what, REDBLACK_LEFT)
+#define REDBLACK_RIGHT_CHILD(name, what) REDBLACK_CHILD(name, what, REDBLACK_RIGHT)
+#define REDBLACK_PARENT_SIDE(name, parent, child) ((REDBLACK_LEFT_CHILD(name, parent) == child) ? REDBLACK_LEFT : REDBLACK_RIGHT)
+
+
+#define REDBLACK_DUMP(type, name, root, dumper) \
+  do { \
+    type *n = root; \
+    int depth = 0, dir = 0; \
+    while (n) { \
+      switch (dir) { \
+       case 0: if (REDBLACK_LEFT_CHILD(name, n)) { \
+                 ASSERT(REDBLACK_PARENT(type, name, REDBLACK_LEFT_CHILD(name, n)) == n); \
+                 n = REDBLACK_LEFT_CHILD(name, n); dir = 0; depth++; break; \
+               } __attribute__((fallthrough)); \
+       case 1: dumper(n, REDBLACK_NODE_COLOR(name, n), depth); \
+               if (REDBLACK_RIGHT_CHILD(name, n)) { \
+                 ASSERT(REDBLACK_PARENT(type, name, REDBLACK_RIGHT_CHILD(name, n)) == n); \
+                 n = REDBLACK_RIGHT_CHILD(name, n); dir = 0; depth++; break; \
+               } __attribute__((fallthrough)); \
+       case 2: { \
+                 type *p = REDBLACK_PARENT(type, name, n); \
+                 if (p) dir = REDBLACK_PARENT_SIDE(name, p, n); \
+                 n = p; \
+                 depth--; \
+                 break; \
+               } \
+      } \
+    } \
+  } while (0)
+
+#define REDBLACK_MAX_REASONABLE_DEPTH 256
+#define REDBLACK_CHECK(type, name, key, compare, root) do { \
+  if (!root) \
+    break; \
+  type *prev = NULL; \
+  struct redblack_check { \
+    type *node; \
+    int state; \
+    int blackness[2]; \
+  } stack[REDBLACK_MAX_REASONABLE_DEPTH] = { \
+    {}, \
+    { .node = root } \
+  }; \
+  int pos = 1; \
+  while (pos > 0) { \
+    switch (stack[pos].state) { \
+      case 0: \
+       if (REDBLACK_LEFT_CHILD(name, stack[pos].node)) { \
+         stack[pos+1] = (struct redblack_check) { .node = REDBLACK_LEFT_CHILD(name, stack[pos].node) }; \
+         ASSERT(compare(key(stack[pos+1].node), key(stack[pos].node)) < 0); \
+         pos++; \
+         continue; \
+       } \
+       stack[pos].state++; \
+       __attribute__((fallthrough)); \
+      case 1: \
+       ASSERT(!prev || (compare(key(prev), key(stack[pos].node)) < 0)); \
+       if (REDBLACK_RIGHT_CHILD(name, stack[pos].node)) { \
+         stack[pos+1] = (struct redblack_check) { .node = REDBLACK_RIGHT_CHILD(name, stack[pos].node) }; \
+         ASSERT(compare(key(stack[pos+1].node), key(stack[pos].node)) > 0); \
+         pos++; \
+         continue; \
+       } \
+       stack[pos].state++; \
+       __attribute__((fallthrough)); \
+      case 2: \
+       ASSERT(stack[pos].blackness[0] == stack[pos].blackness[1]); \
+       stack[pos-1].blackness[stack[pos-1].state] = stack[pos].blackness[0] + (REDBLACK_NODE_COLOR(name, stack[pos].node) == REDBLACK_BLACK); \
+       pos--; \
+       stack[pos].state++; \
+    } \
+  } \
+/*  printf("Redblack check OK. Overall blackness: %d\n", stack[0].blackness[0]); */ \
+} while(0)
+
+#define REDBLACK_FIND_POINTER(name, key, compare, root, what, pointer) \
+  for ( \
+      int _cmp = !(pointer = &(root)); \
+      (*pointer) && (_cmp = compare((what), key((*pointer)))); \
+      pointer = &(REDBLACK_CHILD(name, (*pointer), ((_cmp < 0) ? 1 : 2))) \
+      )
+
+#define REDBLACK_FIND(type, name, key, compare, root, what) \
+  ({ type **pointer; REDBLACK_FIND_POINTER(name, key, compare, root, what, pointer); *pointer; })
+
+#define REDBLACK_FIRST(type, name, root) ({ \
+    type *first = root; \
+    if (first) \
+      while (REDBLACK_LEFT_CHILD(name, first)) \
+       first = REDBLACK_LEFT_CHILD(name, first); \
+    first; \
+})
+
+#define REDBLACK_NEXT(type, name, node) ({ \
+    type *where = node; \
+    if (REDBLACK_RIGHT_CHILD(name, where)) { \
+      where = REDBLACK_RIGHT_CHILD(name, where); \
+      while (REDBLACK_LEFT_CHILD(name, where)) \
+       where = REDBLACK_LEFT_CHILD(name, where); \
+    } else \
+      while (1) { \
+       type *p = REDBLACK_PARENT(type, name, where); \
+       int ps = p ? REDBLACK_PARENT_SIDE(name, p, where) : 0; \
+       where = p; \
+       if (ps == REDBLACK_RIGHT) \
+         continue; \
+       break; \
+      } \
+    where; \
+})
+
+
+/* Low level tree manipulation */
+
+/* Connect a node @ch to its new parent @p on side @side, setting color of @ch to @color */
+#define REDBLACK_CONNECT_NODE_SET_COLOR(type, name, p, side, ch, color) \
+  (ch ? (REDBLACK_PARENT_POINTER(name, (REDBLACK_CHILD(name, p, side) = ch)) = REDBLACK_PTR_COMPOSE(type, p, color)) \
+   : (REDBLACK_CHILD(name, p, side) = NULL))
+
+/* Connect a node @ch to its new parent @p on side @side, keeping its former color */
+#define REDBLACK_CONNECT_NODE_KEEP_COLOR(type, name, p, side, ch) \
+  REDBLACK_CONNECT_NODE_SET_COLOR(type, name, p, side, ch, REDBLACK_NODE_COLOR(name, ch))
+
+/* Opposite side macros (left <=> right) */
+#define REDBLACK_OPPOSITE(side) (3-(side))
+
+/* Tree rotation in a given direction.
+ * Left rotation:
+ *
+ *   (P)                 (C)
+ *  /  XX               XX  \
+ * (A)  (C)    --->   (P)   (D)    
+ *      X \           / X
+ *    (B) (D)       (A) (B)
+ *
+ * Right rotation is in the opposite direction.
+ */
+#define REDBLACK_ROTATE(type, name, root, p, side) do { \
+  type *rp = p, \
+  *rg = REDBLACK_PARENT(type, name, rp), \
+  *rc = REDBLACK_CHILD(name, rp, REDBLACK_OPPOSITE(side)), \
+  *rb = REDBLACK_CHILD(name, rc, side); \
+  int rgs = rg ? REDBLACK_PARENT_SIDE(name, rg, rp) : 0; \
+  REDBLACK_CONNECT_NODE_KEEP_COLOR(type, name, rc, side, rp); \
+  REDBLACK_CONNECT_NODE_KEEP_COLOR(type, name, rp, REDBLACK_OPPOSITE(side), rb); \
+  if (rgs) \
+    REDBLACK_CONNECT_NODE_KEEP_COLOR(type, name, rg, rgs, rc); \
+  else { \
+    REDBLACK_PARENT_POINTER(name, rc) = NULL; \
+    root = rc; \
+  } \
+} while (0)
+
+#define REDBLACK_INSERT(type, name, key, compare, root, what) do { \
+  type **where = &(root); \
+  what->name[1] = what->name[2] = NULL; \
+  REDBLACK_FIND_POINTER(name, key, compare, root, key(what), where) \
+    REDBLACK_PARENT_POINTER(name, what) = REDBLACK_PTR_RED(type, *where); \
+  ASSERT(!*where); \
+  *where = what; \
+  type *n = *where; \
+  do { \
+    if (((uintptr_t) REDBLACK_PARENT_POINTER(name, n)) == 1) \
+      REDBLACK_PARENT_POINTER(name, n) = NULL; \
+    if (REDBLACK_PARENT_POINTER(name, n) == NULL) \
+      break; \
+    type *p = REDBLACK_PARENT(type, name, n); \
+    if (REDBLACK_NODE_COLOR(name, p) == REDBLACK_BLACK) \
+      break; \
+    type *g = REDBLACK_PARENT(type, name, p); \
+    type *u = REDBLACK_CHILD(name, g, REDBLACK_OPPOSITE(REDBLACK_PARENT_SIDE(name, g, p))); \
+    if (u && REDBLACK_NODE_COLOR(name, u) == REDBLACK_RED) { \
+      REDBLACK_SET_COLOR(type, name, u, REDBLACK_BLACK); \
+      REDBLACK_SET_COLOR(type, name, p, REDBLACK_BLACK); \
+      REDBLACK_SET_COLOR(type, name, g, REDBLACK_RED); \
+      n = g; \
+      continue; \
+    } \
+    int gc = REDBLACK_PARENT_SIDE(name, g, p); \
+    int pc = REDBLACK_PARENT_SIDE(name, p, n); \
+    if (gc != pc) { \
+      REDBLACK_ROTATE(type, name, root, p, gc); \
+      REDBLACK_SET_COLOR(type, name, n, REDBLACK_BLACK); \
+    } else \
+      REDBLACK_SET_COLOR(type, name, p, REDBLACK_BLACK); \
+    REDBLACK_ROTATE(type, name, root, g, REDBLACK_OPPOSITE(gc)); \
+    REDBLACK_SET_COLOR(type, name, g, REDBLACK_RED); \
+    break; \
+  } while (1); \
+} while (0)
+
+#define REDBLACK_EXCHANGE(type, name, root, aa, bb) do { \
+  type *a = aa, *b = bb; \
+  type *ap = REDBLACK_PARENT(type, name, a), *al = REDBLACK_LEFT_CHILD(name, a), *ar = REDBLACK_RIGHT_CHILD(name, a); \
+  type *bp = REDBLACK_PARENT(type, name, b), *bl = REDBLACK_LEFT_CHILD(name, b), *br = REDBLACK_RIGHT_CHILD(name, b); \
+  int as = ap ? REDBLACK_PARENT_SIDE(name, ap, a) : 0, bs = bp ? REDBLACK_PARENT_SIDE(name, bp, b) : 0; \
+  if ((ap != b) || (as != REDBLACK_LEFT)) \
+    REDBLACK_CONNECT_NODE_KEEP_COLOR(type, name, a, REDBLACK_LEFT, bl); \
+  if ((bp != a) || (bs != REDBLACK_LEFT)) \
+    REDBLACK_CONNECT_NODE_KEEP_COLOR(type, name, b, REDBLACK_LEFT, al); \
+  if ((ap != b) || (as != REDBLACK_RIGHT)) \
+    REDBLACK_CONNECT_NODE_KEEP_COLOR(type, name, a, REDBLACK_RIGHT, br); \
+  if ((bp != a) || (bs != REDBLACK_RIGHT)) \
+    REDBLACK_CONNECT_NODE_KEEP_COLOR(type, name, b, REDBLACK_RIGHT, ar); \
+  int ac = REDBLACK_NODE_COLOR(name, a), bc = REDBLACK_NODE_COLOR(name, b); \
+  if (a == bp) \
+    bp = b; \
+  if (b == ap) \
+    ap = a; \
+  if (ap) \
+    REDBLACK_CONNECT_NODE_SET_COLOR(type, name, ap, as, b, ac); \
+  else { \
+    REDBLACK_PARENT_POINTER(name, b) = NULL; \
+    root = b; \
+  } \
+  if (bp) \
+    REDBLACK_CONNECT_NODE_SET_COLOR(type, name, bp, bs, a, bc); \
+  else { \
+    REDBLACK_PARENT_POINTER(name, a) = NULL; \
+    root = a; \
+  } \
+} while (0)
+
+#define REDBLACK_DELETE(type, name, root, what_) do { \
+  type *what = what_; \
+  type *cl = REDBLACK_LEFT_CHILD(name, what); \
+  type *cr = REDBLACK_RIGHT_CHILD(name, what); \
+  if (cl && cr) { \
+    type *s = cl; \
+    while (REDBLACK_RIGHT_CHILD(name, s)) s = REDBLACK_RIGHT_CHILD(name, s); \
+    REDBLACK_EXCHANGE(type, name, root, s, what); \
+    cl = REDBLACK_LEFT_CHILD(name, what); \
+    cr = REDBLACK_RIGHT_CHILD(name, what); \
+  } \
+  type *p = REDBLACK_PARENT(type, name, what); \
+  if (REDBLACK_NODE_COLOR(name, what) == REDBLACK_RED) { \
+    ASSERT((cl == NULL) && (cl == NULL)); \
+    REDBLACK_CHILD(name, p, REDBLACK_PARENT_SIDE(name, p, what)) = NULL; \
+    break; \
+  } \
+  /* The only child now must be red */ \
+  int ps = REDBLACK_PARENT_SIDE(name, p, what); \
+  if (cl) { \
+    REDBLACK_CONNECT_NODE_SET_COLOR(type, name, p, ps, cl, REDBLACK_BLACK); \
+    break; \
+  } \
+  if (cr) { \
+    REDBLACK_CONNECT_NODE_SET_COLOR(type, name, p, ps, cr, REDBLACK_BLACK); \
+    break; \
+  } \
+  type *drop = what; \
+  while (1) { /* Invariant: what is black */ \
+    if (what == root) { /* Case 1 */ \
+      root = NULL; \
+      break; \
+    } \
+    type *p = REDBLACK_PARENT(type, name, what); \
+    int ws = REDBLACK_PARENT_SIDE(name, p, what); \
+    type *s = REDBLACK_CHILD(name, p, REDBLACK_OPPOSITE(ws)); \
+    /* Case 2 */ \
+    if (s && (REDBLACK_NODE_COLOR(name, s) == REDBLACK_RED)) { /* Therefore p is black also in case 2 */ \
+      REDBLACK_ROTATE(type, name, root, p, ws); \
+      REDBLACK_SET_COLOR(type, name, p, REDBLACK_RED); \
+      REDBLACK_SET_COLOR(type, name, s, REDBLACK_BLACK); \
+      continue; \
+    } \
+    if (drop) drop = REDBLACK_CHILD(name, p, ws) = NULL; \
+    type *sc[2] = REDBLACK_CHILDREN(name, s); \
+    /* Case 3 & 4: sc[0] and sc[1] are both black; s is black from case 2 */ \
+    if ((!sc[0] || REDBLACK_NODE_COLOR(name, sc[0]) == REDBLACK_BLACK) && \
+       (!sc[1] || REDBLACK_NODE_COLOR(name, sc[1]) == REDBLACK_BLACK)) { \
+      if (REDBLACK_NODE_COLOR(name, p) == REDBLACK_BLACK) { /* Case 3 */ \
+       /* No red node nearby, pushing the change up the tree */ \
+       REDBLACK_SET_COLOR(type, name, s, REDBLACK_RED); \
+       what = p; \
+       continue; \
+      } else { /* Case 4: p is red */ \
+       /* Moving the red down the other tree */ \
+       REDBLACK_SET_COLOR(type, name, p, REDBLACK_BLACK); \
+       REDBLACK_SET_COLOR(type, name, s, REDBLACK_RED); \
+       break; \
+      } \
+    } \
+    /* Now sc[0] or sc[1] must be red (one or both) */ \
+    /* Case 5: the niece on the opposite side is black */ \
+    int nop = (REDBLACK_OPPOSITE(ws) == REDBLACK_RIGHT); \
+    if (!sc[nop] || (REDBLACK_NODE_COLOR(name, sc[nop]) == REDBLACK_BLACK)) { \
+      REDBLACK_SET_COLOR(type, name, s, REDBLACK_RED); \
+      REDBLACK_SET_COLOR(type, name, sc[1-nop], REDBLACK_BLACK); \
+      REDBLACK_ROTATE(type, name, root, s, REDBLACK_OPPOSITE(ws)); \
+      s = REDBLACK_CHILD(name, p, REDBLACK_OPPOSITE(ws)); \
+      sc[0] = REDBLACK_LEFT_CHILD(name, s); \
+      sc[1] = REDBLACK_RIGHT_CHILD(name, s); \
+    } \
+    /* Case 6: the niece on the opposite side is red */ \
+    REDBLACK_ROTATE(type, name, root, p, ws); \
+    REDBLACK_SET_COLOR(type, name, s, REDBLACK_NODE_COLOR(name, p)); \
+    REDBLACK_SET_COLOR(type, name, p, REDBLACK_BLACK); \
+    REDBLACK_SET_COLOR(type, name, sc[nop], REDBLACK_BLACK); \
+    break; \
+  } \
+} while (0) 
+
+#endif
diff --git a/lib/redblack_test.c b/lib/redblack_test.c
new file mode 100644 (file)
index 0000000..5e39b3b
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ *     BIRD Library -- Red Black Tree Tests
+ *
+ *     (c) 2018 Maria Matejka <mq@jmq.cz>
+ *     (c) 2018 CZ.NIC z.s.p.o.
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "test/birdtest.h"
+#include "lib/redblack.h"
+
+struct rb_test {
+  REDBLACK_NODE(struct rb_test, rb_);
+  int value;
+};
+
+#define RBT_KEY(a) ((a)->value)
+#define RBT_COMPARE(a, b) ((a) - (b))
+
+#define RBTDS64 "                                                                "
+const char *spaces = RBTDS64;
+
+#define RBT_DUMPER(node, color, depth) printf("%s%c %d\n", spaces + 64 - (depth*2), color ? '-' : '*', node->value); fflush(stdout)
+
+static void rb_dump(struct rb_test *root) {
+  printf("Redblack dump.\n");
+  REDBLACK_DUMP(struct rb_test, rb_, root, RBT_DUMPER);
+  printf("Redblack dump done.\n");
+  fflush(stdout);
+}
+
+#define RB_CHECK(root) do { \
+  REDBLACK_CHECK(struct rb_test, rb_, RBT_KEY, RBT_COMPARE, root); \
+  for ( \
+      struct rb_test *last = NULL, *node = REDBLACK_FIRST(struct rb_test, rb_, root); \
+      node; \
+      last = node, node = REDBLACK_NEXT(struct rb_test, rb_, node) \
+      ) \
+    if (last) \
+      ASSERT(RBT_COMPARE(RBT_KEY(last), RBT_KEY(node)) < 0); \
+} while (0)
+
+#define RB_FIND(root, val, exists) do { \
+  struct rb_test *found = REDBLACK_FIND(struct rb_test, rb_, RBT_KEY, RBT_COMPARE, root, val); \
+  if (exists) { \
+    bt_assert(found); \
+    bt_assert(found->value == val); \
+  } else \
+    bt_assert(!found); \
+} while (0)
+
+#define RB_INSERT(root, val) do { \
+  struct rb_test *new = xmalloc(sizeof(struct rb_test)); \
+  *new = (struct rb_test) { .value = val }; \
+  REDBLACK_INSERT(struct rb_test, rb_, RBT_KEY, RBT_COMPARE, root, new); \
+} while (0)
+
+#define RB_DELETE(root, val) do { \
+  struct rb_test *old = REDBLACK_FIND(struct rb_test, rb_, RBT_KEY, RBT_COMPARE, root, val); \
+  REDBLACK_DELETE(struct rb_test, rb_, root, old); \
+} while (0)
+
+static int
+rb_insert(void)
+{
+  struct rb_test *root = NULL;
+
+#define N 4096
+#define MUL 16
+#define BIT(x) ((bits[(x) / 64] >> ((x) % 64)) & 1)
+#define SIT(x) (bits[(x) / 64] |= (1ULL << ((x) % 64)))
+#define CIT(x) (bits[(x) / 64] &= ~(1ULL << ((x) % 64)))
+
+  u64 bits[N / 64] = {};
+  for (int i=0; i<N * MUL; i++) {
+    RB_CHECK(root);
+    if (bt_verbose >= BT_VERBOSE_ABSOLUTELY_ALL)
+      rb_dump(root);
+
+    int tv = bt_random() % N;
+    RB_FIND(root, tv, BIT(tv));
+
+    if (BIT(tv)) {
+      bt_debug("Deleting existing value %d\n", tv);
+      fflush(stdout);
+      CIT(tv);
+      RB_DELETE(root, tv);
+    } else {
+      bt_debug("Inserting value %d\n", tv);
+      fflush(stdout);
+      SIT(tv);
+      RB_INSERT(root, tv);
+    }
+  }
+  return 1;  
+}
+
+int
+main(int argc, char **argv)
+{
+  bt_init(argc, argv);
+  bt_test_suite_extra(rb_insert, BT_FORKING, 30, "redblack insertion test");
+}