/* $Id: itree.C,v 1.4 1998/11/27 04:20:45 dm Exp $ */

/*
 *
 * Copyright (C) 1998 David Mazieres (dm@uun.org)
 *
 * 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, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 */


#include "itree.h"

#define oc __opaquecontainer_pointer
#define itree_entry __itree_entry_private

/* Define some gross macros to avoid a lot of typing.  These asssume a
 * local variable 'os' contains the offset of the itree_entry
 * structure in the opaque container structure. */
static inline struct itree_entry *oc2rb (oc, const int)
     __attribute__ ((const));
static inline struct itree_entry *
oc2rb (oc o, const int os)
{
  return (struct itree_entry *) ((char *) o + os);
}
#define up(n) oc2rb (n, os)->rbe_up
#define left(n) oc2rb (n, os)->rbe_left
#define right(n) oc2rb (n, os)->rbe_right
#define color(n) ((n) ? oc2rb (n, os)->rbe_color : BLACK)
#define lcolor(n) oc2rb (n, os)->rbe_color

static void
itree_left_rotate (oc *r, oc x, const int os)
{
  oc y = right (x);
  if ((right (x) = left (y)))
    up (left (y)) = x;
  up (y) = up (x);
  if (!up (x))
    *r = y;
  else if (x == left (up (x)))
    left (up (x)) = y;
  else
    right (up (x)) = y;
  left (y) = x;
  up (x) = y;
}

static void
itree_right_rotate (oc *r, oc y, const int os)
{
  oc x = left (y);
  if ((left (y) = right (x)))
    up (right (x)) = y;
  up (x) = up (y);
  if (!up (y))
    *r = x;
  else if (y == right (up (y)))
    right (up (y)) = x;
  else
    left (up (y)) = x;
  right (x) = y;
  up (y) = x;
}

/* Turn any newly inserted node red.  Then fix things up if the new
 * node's parent is also red.  Note that this routine assumes the root
 * of the tree is black. */
static inline void
itree_insert_fix (oc *r, oc x, const int os)
{
  oc y;

  lcolor (x) = RED;
  while (color (up (x)) == RED) {
    oc gp = up (up (x));
    if (up (x) == left (gp)) {
      y = right (gp);
      if (color (y) == RED) {
	lcolor (up (x)) = BLACK;
	lcolor (y) = BLACK;
	lcolor (gp) = RED;
	x = gp;
      }
      else {
	if (x == right (up (x))) {
	  x = up (x);
	  itree_left_rotate (r, x, os);
	}
	lcolor (up (x)) = BLACK;
	lcolor (up (up (x))) = RED;
	itree_right_rotate (r, up (up (x)), os);
      }
    }
    else {
      y = left (gp);
      if (color (y) == RED) {
	lcolor (up (x)) = BLACK;
	lcolor (y) = BLACK;
	lcolor (gp) = RED;
	x = gp;
      }
      else {
	if (x == left (up (x))) {
	  x = up (x);
	  itree_right_rotate (r, x, os);
	}
	lcolor (up (x)) = BLACK;
	lcolor (up (up (x))) = RED;
	itree_left_rotate (r, up (up (x)), os);
      }
    }
  }
  lcolor (*r) = BLACK;
}

void
itree_insert (oc *r, oc z, const int os,
	      int (*cmpfn) (void *, oc, oc), void *cmparg)
{
  oc y, x;
  int cmpres = 0;

  left (z) = right (z) = y = NULL;
  for (x = *r; x; x = cmpres < 0 ? left (x) : right (x)) {
    y = x;
    cmpres = cmpfn (cmparg, z, x);
  }
  up (z) = y;
  if (!y)
    *r = z;
  else if (cmpres < 0)
    left (y) = z;
  else
    right (y) = z;
  itree_insert_fix (r, z, os);
}

static inline oc
itree_minimum (oc x, const int os)
{
  oc y;
  while ((y = left (x)))
    x = y;
  return x;
}

oc
itree_successor (oc x, const int os)
{
  oc y;
  if ((y = right (x)))
    return itree_minimum (y, os);
  for (y = up (x); y && x == right (y); y = up (y))
    x = y;
  return y;
}

static inline oc
itree_maximum (oc x, const int os)
{
  oc y;
  while ((y = right (x)))
    x = y;
  return x;
}

oc
itree_predecessor (oc x, const int os)
{
  oc y;
  if ((y = left (x)))
    return itree_maximum (y, os);
  for (y = up (x); y && x == left (y); y = up (y))
    x = y;
  return y;
}

static inline void
itree_delete_fixup (oc *r, oc x, oc p, const int os)
{
  oc w;

  assert (!x || up (x) == p);
  while (x != *r && color (x) == BLACK) {
    if (x)
      p = up (x);
    if (x == left (p)) {
      w = right (p);
      if (color (w) == RED) {
	lcolor (w) = BLACK;
	lcolor (p) = RED;
	itree_left_rotate (r, p, os);
	w = right (p);
      }
      if (color (left (w)) == BLACK && color (right (w)) == BLACK) {
	lcolor (w) = RED;
	x = p;
      }
      else {
	if (color (right (w)) == BLACK) {
	  lcolor (left (w)) = BLACK;
	  lcolor (w) = RED;
	  itree_right_rotate (r, w, os);
	  w = right (p);
	}
	lcolor (w) = color (p);
	lcolor (p) = BLACK;
	lcolor (right (w)) = BLACK;
	itree_left_rotate (r, p, os);
	x = *r;
      }
    }
    else {
      w = left (p);
      if (color (w) == RED) {
	lcolor (w) = BLACK;
	lcolor (p) = RED;
	itree_right_rotate (r, p, os);
	w = left (p);
      }
      if (color (right (w)) == BLACK && color (left (w)) == BLACK) {
	lcolor (w) = RED;
	x = p;
      }
      else {
	if (color (left (w)) == BLACK) {
	  lcolor (right (w)) = BLACK;
	  lcolor (w) = RED;
	  itree_left_rotate (r, w, os);
	  w = left (p);
	}
	lcolor (w) = color (p);
	lcolor (p) = BLACK;
	lcolor (left (w)) = BLACK;
	itree_right_rotate (r, p, os);
	x = *r;
      }
    }
  }
  if (x)
    lcolor (x) = BLACK;
}

void
itree_delete (oc *r, oc z, const int os)
{
  oc x, y, p;
  enum itree_color c;

  y = left (z) && right (z) ? itree_successor (z, os) : z;
  p = up (y);
  if ((x = left (y)) || (x = right (y)))
    up (x) = p;
  if (!p)
    *r = x;
  else if (y == left (p))
    left (p) = x;
  else
    right (p) = x;
  c = color (y);

  if (y != z) {
    oc pz = up (z);
    if (pz) {
      if (z == left (pz))
	left (pz) = y;
      else
	right (pz) = y;
    }
    else
      *r = y;
    if ((pz = left (z)))
      up (pz) = y;
    if ((pz = right (z)))
      up (pz) = y;
    *oc2rb (y, os) = *oc2rb (z, os);
    if (p == z)
      p = y;
  }

  if (c == BLACK)
    itree_delete_fixup (r, x, p, os);
}

static void
itree_check_node (oc x, oc low, oc high, int bd, const int lbd,
		  const int os,
		  int (*cmpfn) (void *, oc, oc), void *cmparg)
{
  volatile oc l, r, p;
  volatile enum itree_color cx, cl, cr, cp;

  if (color (x) == BLACK)
    bd++;
  if (!x) {
    assert (bd == lbd);
    return;
  }

  cx = color (x);
  p = up (x);
  cp = color (p);
  l = left (x);
  cl = color (l);
  r = right (x);
  cr = color (r);

  assert (!l || up (l) == x);
  assert (!r || up (r) == x);
  assert (cx == BLACK || cx == RED);
  assert (cx == BLACK || (cl == BLACK && cr == BLACK));
  assert (!low || cmpfn (cmparg, low, x) <= 0);
  assert (!high || cmpfn (cmparg, x, high) <= 0);

  itree_check_node (l, low, x, bd, lbd, os, cmpfn, cmparg);
  itree_check_node (r, x, high, bd, lbd, os, cmpfn, cmparg);
}

void
__itree_check (oc *r, const int os,
	       int (*cmpfn) (void *, oc, oc), void *cmparg)
{
  oc x;
  int lbd = 0;

  assert (color (*r) == BLACK);
  for (x = *r; x;) {
    x = left (x);
    if (color (x) == BLACK)
      lbd++;
  }

  assert (!*r || !up (*r));
  itree_check_node (*r, NULL, NULL, -1, lbd, os, cmpfn, cmparg);
}
