/* Deletes from |tree| and returns an item matching |item|.
   Returns a null pointer if no matching item found. */
void *
rtavl_delete (struct rtavl_table *tree, const void *item)
{
  /* Stack of nodes. */
  struct rtavl_node *pa[RTAVL_MAX_HEIGHT]; /* Nodes. */
  unsigned char da[RTAVL_MAX_HEIGHT];     /* |rtavl_link[]| indexes. */
  int k;                                  /* Stack pointer. */

  struct rtavl_node *p; /* Traverses tree to find node to delete. */

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

  k = 1;
  da[0] = 0;
  pa[0] = (struct rtavl_node *) &tree->rtavl_root;
  p = tree->rtavl_root;
  if (p == NULL)
    return NULL;

  for (;;)
    {
      int cmp, dir;

      cmp = tree->rtavl_compare (item, p->rtavl_data, tree->rtavl_param);
      if (cmp == 0)
        break;

      dir = cmp > 0;
      if (dir == 0)
        {
          if (p->rtavl_link[0] == NULL)
            return NULL;
        }
      else /* |dir == 1| */
        {
          if (p->rtavl_rtag == RTAVL_THREAD)
            return NULL;
        }

      pa[k] = p;
      da[k++] = dir;
      p = p->rtavl_link[dir];
    }
  tree->rtavl_count--;
  item = p->rtavl_data;

  if (p->rtavl_link[0] == NULL)
    {
      if (p->rtavl_rtag == RTAVL_CHILD)
        {
          pa[k - 1]->rtavl_link[da[k - 1]] = p->rtavl_link[1];
        }
      else
        {
          pa[k - 1]->rtavl_link[da[k - 1]] = p->rtavl_link[da[k - 1]];
          if (da[k - 1] == 1)
            pa[k - 1]->rtavl_rtag = RTAVL_THREAD;
        }
    }
  else
    {
      struct rtavl_node *r = p->rtavl_link[0];
      if (r->rtavl_rtag == RTAVL_THREAD)
        {
          r->rtavl_link[1] = p->rtavl_link[1];
          r->rtavl_rtag = p->rtavl_rtag;
          r->rtavl_balance = p->rtavl_balance;
          pa[k - 1]->rtavl_link[da[k - 1]] = r;
          da[k] = 0;
          pa[k++] = r;
        }
      else
        {
          struct rtavl_node *s;
          int j = k++;

          for (;;)
            {
              da[k] = 1;
              pa[k++] = r;
              s = r->rtavl_link[1];
              if (s->rtavl_rtag == RTAVL_THREAD)
                break;

              r = s;
            }

          da[j] = 0;
          pa[j] = pa[j - 1]->rtavl_link[da[j - 1]] = s;

          if (s->rtavl_link[0] != NULL)
            r->rtavl_link[1] = s->rtavl_link[0];
          else
            {
              r->rtavl_rtag = RTAVL_THREAD;
              r->rtavl_link[1] = s;
            }

          s->rtavl_balance = p->rtavl_balance;
          s->rtavl_link[0] = p->rtavl_link[0];
          s->rtavl_link[1] = p->rtavl_link[1];
          s->rtavl_rtag = p->rtavl_rtag;
        }
    }

  tree->rtavl_alloc->libavl_free (tree->rtavl_alloc, p);

  assert (k > 0);
  while (--k > 0)
    {
      struct rtavl_node *y = pa[k];

      if (da[k] == 0)
        {
          y->rtavl_balance++;
          if (y->rtavl_balance == +1)
            break;
          else if (y->rtavl_balance == +2)
            {
              struct rtavl_node *x = y->rtavl_link[1];

              assert (x != NULL);
              if (x->rtavl_balance == -1)
                {
                  struct rtavl_node *w;

                  assert (x->rtavl_balance == -1);
                  w = x->rtavl_link[0];
                  x->rtavl_link[0] = w->rtavl_link[1];
                  w->rtavl_link[1] = x;
                  y->rtavl_link[1] = w->rtavl_link[0];
                  w->rtavl_link[0] = y;
                  if (w->rtavl_balance == +1)
                    x->rtavl_balance = 0, y->rtavl_balance = -1;
                  else if (w->rtavl_balance == 0)
                    x->rtavl_balance = y->rtavl_balance = 0;
                  else /* |w->rtavl_balance == -1| */
                    x->rtavl_balance = +1, y->rtavl_balance = 0;
                  w->rtavl_balance = 0;
                  if (y->rtavl_link[1] == NULL)
                    {
                      y->rtavl_rtag = RTAVL_THREAD;
                      y->rtavl_link[1] = w;
                    }
                  if (w->rtavl_rtag == RTAVL_THREAD)
                    {
                      x->rtavl_link[0] = NULL;
                      w->rtavl_rtag = RTAVL_CHILD;
                    }
                  pa[k - 1]->rtavl_link[da[k - 1]] = w;
                }
              else
                {
                  pa[k - 1]->rtavl_link[da[k - 1]] = x;
                  if (x->rtavl_balance == 0)
                    {
                      y->rtavl_link[1] = x->rtavl_link[0];
                      x->rtavl_link[0] = y;
                      x->rtavl_balance = -1;
                      y->rtavl_balance = +1;
                      break;
                    }
                  else /* |x->rtavl_balance == +1| */
                    {
                      if (x->rtavl_link[0] != NULL)
                        y->rtavl_link[1] = x->rtavl_link[0];
                      else
                        y->rtavl_rtag = RTAVL_THREAD;
                      x->rtavl_link[0] = y;
                      y->rtavl_balance = x->rtavl_balance = 0;
                    }
                }
            }
        }
      else
        {
          y->rtavl_balance--;
          if (y->rtavl_balance == -1)
            break;
          else if (y->rtavl_balance == -2)
            {
              struct rtavl_node *x = y->rtavl_link[0];

              assert (x != NULL);
              if (x->rtavl_balance == +1)
                {
                  struct rtavl_node *w;

                  assert (x->rtavl_balance == +1);
                  w = x->rtavl_link[1];
                  x->rtavl_link[1] = w->rtavl_link[0];
                  w->rtavl_link[0] = x;
                  y->rtavl_link[0] = w->rtavl_link[1];
                  w->rtavl_link[1] = y;
                  if (w->rtavl_balance == -1)
                    x->rtavl_balance = 0, y->rtavl_balance = +1;
                  else if (w->rtavl_balance == 0)
                    x->rtavl_balance = y->rtavl_balance = 0;
                  else /* |w->rtavl_balance == +1| */
                    x->rtavl_balance = -1, y->rtavl_balance = 0;
                  w->rtavl_balance = 0;
                  if (x->rtavl_link[1] == NULL)
                    {
                      x->rtavl_rtag = RTAVL_THREAD;
                      x->rtavl_link[1] = w;
                    }
                  if (w->rtavl_rtag == RTAVL_THREAD)
                    {
                      y->rtavl_link[0] = NULL;
                      w->rtavl_rtag = RTAVL_CHILD;
                    }
                  pa[k - 1]->rtavl_link[da[k - 1]] = w;
                }
              else
                {
                  pa[k - 1]->rtavl_link[da[k - 1]] = x;
                  if (x->rtavl_balance == 0)
                    {
                      y->rtavl_link[0] = x->rtavl_link[1];
                      x->rtavl_link[1] = y;
                      x->rtavl_balance = +1;
                      y->rtavl_balance = -1;
                      break;
                    }
                  else /* |x->rtavl_balance == -1| */
                    {
                      if (x->rtavl_rtag == RTAVL_CHILD)
                        y->rtavl_link[0] = x->rtavl_link[1];
                      else
                        {
                          y->rtavl_link[0] = NULL;
                          x->rtavl_rtag = RTAVL_CHILD;
                        }
                      x->rtavl_link[1] = y;
                      y->rtavl_balance = x->rtavl_balance = 0;
                    }
                }
            }
        }
    }

  return (void *) item;
}

