/* Inserts |item| into |tree| and returns a pointer to |item|'s address.
   If a duplicate item is found in the tree,
   returns a pointer to the duplicate without inserting |item|.
   Returns |NULL| in case of memory allocation failure. */
void **
rtrb_probe (struct rtrb_table *tree, void *item)
{
  struct rtrb_node *pa[RTRB_MAX_HEIGHT]; /* Nodes on stack. */
  unsigned char da[RTRB_MAX_HEIGHT];   /* Directions moved from stack nodes. */
  int k;                               /* Stack height. */

  struct rtrb_node *p; /* Current node in search. */
  struct rtrb_node *n; /* New node. */
  int dir;             /* Side of |p| on which |p| is located. */

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

  da[0] = 0;
  pa[0] = (struct rtrb_node *) &tree->rtrb_root;
  k = 1;
  if (tree->rtrb_root != NULL)
    for (p = tree->rtrb_root; ; p = p->rtrb_link[dir])
      {
        int cmp = tree->rtrb_compare (item, p->rtrb_data, tree->rtrb_param);
        if (cmp == 0)
          return &p->rtrb_data;

        pa[k] = p;
        da[k++] = dir = cmp > 0;

        if (dir == 0)
          {
            if (p->rtrb_link[0] == NULL)
              break;
          }
        else /* |dir == 1| */
          {
            if (p->rtrb_rtag == RTRB_THREAD)
              break;
          }
      }
  else
    {
      p = (struct rtrb_node *) &tree->rtrb_root;
      dir = 0;
    }

  n = tree->rtrb_alloc->libavl_malloc (tree->rtrb_alloc, sizeof *n);
  if (n == NULL)
    return NULL;

  tree->rtrb_count++;
  n->rtrb_data = item;
  n->rtrb_link[0] = NULL;
  if (dir == 0)
    {
      if (tree->rtrb_root != NULL)
        n->rtrb_link[1] = p;
      else
        n->rtrb_link[1] = NULL;
    }
  else /* |dir == 1| */
    {
      p->rtrb_rtag = RTRB_CHILD;
      n->rtrb_link[1] = p->rtrb_link[1];
    }
  n->rtrb_rtag = RTRB_THREAD;
  n->rtrb_color = RTRB_RED;
  p->rtrb_link[dir] = n;

  while (k >= 3 && pa[k - 1]->rtrb_color == RTRB_RED)
    {
      if (da[k - 2] == 0)
        {
          struct rtrb_node *y = pa[k - 2]->rtrb_link[1];
          if (pa[k - 2]->rtrb_rtag == RTRB_CHILD && y->rtrb_color == RTRB_RED)
            {
              pa[k - 1]->rtrb_color = y->rtrb_color = RTRB_BLACK;
              pa[k - 2]->rtrb_color = RTRB_RED;
              k -= 2;
            }
          else
            {
              struct rtrb_node *x;

              if (da[k - 1] == 0)
                y = pa[k - 1];
              else
                {
                  x = pa[k - 1];
                  y = x->rtrb_link[1];
                  x->rtrb_link[1] = y->rtrb_link[0];
                  y->rtrb_link[0] = x;
                  pa[k - 2]->rtrb_link[0] = y;

                  if (x->rtrb_link[1] == NULL)
                    {
                      x->rtrb_rtag = RTRB_THREAD;
                      x->rtrb_link[1] = y;
                    }
                }

              x = pa[k - 2];
              x->rtrb_color = RTRB_RED;
              y->rtrb_color = RTRB_BLACK;

              x->rtrb_link[0] = y->rtrb_link[1];
              y->rtrb_link[1] = x;
              pa[k - 3]->rtrb_link[da[k - 3]] = y;

              if (y->rtrb_rtag == RTRB_THREAD)
                {
                  y->rtrb_rtag = RTRB_CHILD;
                  x->rtrb_link[0] = NULL;
                }
              break;
            }
        }
      else
        {
          struct rtrb_node *y = pa[k - 2]->rtrb_link[0];
          if (pa[k - 2]->rtrb_link[0] != NULL && y->rtrb_color == RTRB_RED)
            {
              pa[k - 1]->rtrb_color = y->rtrb_color = RTRB_BLACK;
              pa[k - 2]->rtrb_color = RTRB_RED;
              k -= 2;
            }
          else
            {
              struct rtrb_node *x;

              if (da[k - 1] == 1)
                y = pa[k - 1];
              else
                {
                  x = pa[k - 1];
                  y = x->rtrb_link[0];
                  x->rtrb_link[0] = y->rtrb_link[1];
                  y->rtrb_link[1] = x;
                  pa[k - 2]->rtrb_link[1] = y;

                  if (y->rtrb_rtag == RTRB_THREAD)
                    {
                      y->rtrb_rtag = RTRB_CHILD;
                      x->rtrb_link[0] = NULL;
                    }
                }

              x = pa[k - 2];
              x->rtrb_color = RTRB_RED;
              y->rtrb_color = RTRB_BLACK;

              x->rtrb_link[1] = y->rtrb_link[0];
              y->rtrb_link[0] = x;
              pa[k - 3]->rtrb_link[da[k - 3]] = y;

              if (x->rtrb_link[1] == NULL)
                {
                  x->rtrb_rtag = RTRB_THREAD;
                  x->rtrb_link[1] = y;
                }
              break;
            }
        }
    }
  tree->rtrb_root->rtrb_color = RTRB_BLACK;

  return &n->rtrb_data;
}

