/* Deletes from |tree| and returns an item matching |item|.
   Returns a null pointer if no matching item found. */
void *
rtbst_delete (struct rtbst_table *tree, const void *item)
{
  struct rtbst_node *p;        /* Node to delete. */
  struct rtbst_node *q;        /* Parent of |p|. */
  int dir;              /* Index into |q->rtbst_link[]| that leads to |p|. */

  assert (tree != NULL && item != NULL);

  if (tree->rtbst_root == NULL)
    return NULL;

  p = tree->rtbst_root;
  q = (struct rtbst_node *) &tree->rtbst_root;
  dir = 0;
  if (p == NULL)
    return NULL;

  for (;;)
    {
      int cmp = tree->rtbst_compare (item, p->rtbst_data, tree->rtbst_param);
      if (cmp == 0)
        break;

      dir = cmp > 0;
      if (dir == 0)
        {
          if (p->rtbst_link[0] == NULL)
            return NULL;
        }
      else /* |dir == 1| */
        {
          if (p->rtbst_rtag == RTBST_THREAD)
            return NULL;
        }

      q = p;
      p = p->rtbst_link[dir];
    }
  item = p->rtbst_data;

  if (p->rtbst_link[0] == NULL)
    {
      if (p->rtbst_rtag == RTBST_CHILD)
        {
          q->rtbst_link[dir] = p->rtbst_link[1];
        }
      else
        {
          q->rtbst_link[dir] = p->rtbst_link[dir];
          if (dir == 1)
            q->rtbst_rtag = RTBST_THREAD;
        }
    }
  else
    {
      struct rtbst_node *r = p->rtbst_link[0];
      if (r->rtbst_rtag == RTBST_THREAD)
        {
          r->rtbst_link[1] = p->rtbst_link[1];
          r->rtbst_rtag = p->rtbst_rtag;
          q->rtbst_link[dir] = r;
        }
      else
        {
          struct rtbst_node *s;

          for (;;)
            {
              s = r->rtbst_link[1];
              if (s->rtbst_rtag == RTBST_THREAD)
                break;

              r = s;
            }

          if (s->rtbst_link[0] != NULL)
            r->rtbst_link[1] = s->rtbst_link[0];
          else
            {
              r->rtbst_link[1] = s;
              r->rtbst_rtag = RTBST_THREAD;
            }

          s->rtbst_link[0] = p->rtbst_link[0];
          s->rtbst_link[1] = p->rtbst_link[1];
          s->rtbst_rtag = p->rtbst_rtag;

          q->rtbst_link[dir] = s;
        }
    }

  tree->rtbst_alloc->libavl_free (tree->rtbst_alloc, p);
  tree->rtbst_count--;
  return (void *) item;
}

