pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
pennmush/po/
pennmush/win32/msvc.net/
pennmush/win32/msvc6/
/**
 * \file strtree.c
 *
 * \brief String tables for PennMUSH.
 *
 * This is a string table implemented as a red-black tree.
 *
 * There are a couple of peculiarities about this implementation:
 *
 * (1) Parent pointers are not stored.  Instead, insertion and
 *     deletion remember the search path used to get to the
 *     current point in the tree, and use that path to determine
 *     parents.
 *
 * (2) A usage count is kept on items in the tree; when items
 *     have 127 concurrent uses, they become permanent, and
 *     may never be fully deleted.
 *
 * (3) The red/black coloring is stored as the low order bit
 *     in the same byte as the usage count (which takes up
 *     the other 7 bits of that byte).
 *
 * (4) The data string is stored directly in the tree node,
 *     instead of hung in a pointer off the node.  This means
 *     that the nodes are of variable size.  What fun.
 *
 * (5) The strings are stored in the table _unaligned_.  If
 *     you try to use this for anything other than strings,
 *     expect alignment problems.
 *
 * This string table is _NOT_ reentrant.  If you try to use this
 * in a multithreaded environment, you will probably get burned.
 */

#include "copyrite.h"

#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "conf.h"
#include "externs.h"

#include "strtree.h"
#include "confmagic.h"

#ifndef UCHAR_MAX
#define UCHAR_MAX 255	/**< Largest unsigned character */
#endif

/* Various constants.  Their import is either bleedingly obvious
 * or explained below. */
#define ST_MAX_DEPTH 64		/**< Max depth of the tree */
#define ST_RED 1		/**< This node is red */
#define ST_BLACK 0		/**< This node is black */
#define ST_COLOR 1		/**< Bit mask for colors */
#define ST_USE_STEP 2
#define ST_USE_LIMIT (UCHAR_MAX - ST_USE_STEP + 1)

/* Here we have a global for the path info, just so we don't
 * eat tons of stack space.  (This code isn't reentrant no
 * matter where we put this, so might as well save stack.)
 * The fixed size of this array puts a limit on the maximum
 * size of the string table... but with ST_MAX_DEPTH == 64,
 * the tree can hold between 4 billion and 8 quintillion
 * strings.  I don't think capacity is a problem.
 */
static StrNode *path[ST_MAX_DEPTH];

unsigned long st_mem = 0;	/**< Memory used by string trees */

static void st_left_rotate(int tree_depth, StrNode **root);
static void st_right_rotate(int tree_depth, StrNode **root);
static void st_print_tree(StrNode *node, int tree_depth, int lead);
static void st_traverse_stats
  (StrNode *node, int *maxdepth, int *mindepth, int *avgdepth, int *leaves,
   unsigned long *perms, unsigned long *nperms);

void st_stats_header(dbref player);
void st_stats(dbref player, StrTree *root, const char *name);
static void delete_node(StrNode *node);

/** Initialize a string tree.
 * \param root pointer to root of string tree.
 */
void
st_init(StrTree *root)
{
  assert(root);
  root->root = NULL;
  root->count = 0;
  root->mem = 0;
}

static void
delete_node(StrNode *node)
{
  if (node->left)
    delete_node(node->left);
  if (node->right)
    delete_node(node->right);
  mush_free(node, "StrNode");
}


/** Clear a string tree.
 * \param root pointer to root of string tree.
 */
void
st_flush(StrTree *root)
{
  if (!root->root)
    return;
  delete_node(root->root);
  root->root = NULL;
  root->count = 0;
  root->mem = 0;
}

/** Header for string tree stats.
 * \param player player to notify with header.
 */
void
st_stats_header(dbref player)
{
  notify(player,
	 "Tree       Entries  Leaves MinDep  Max  Avg   PermEnt     AvgTmpC ~Memory");
}

/** Statistics about the tree.
 * \param player player to notify with header.
 * \param root pointer to root of string tree.
 * \param name name of string tree, for row header.
 */
void
st_stats(dbref player, StrTree *root, const char *name)
{
  unsigned long bytes;
  int maxdepth = 0, mindepth = 0, avgdepth = 0, leaves = 0;
  unsigned long perms = 0, nperms = 0;

  bytes = (sizeof(StrNode) - BUFFER_LEN) * root->count + root->mem;
  st_traverse_stats(root->root, &maxdepth, &mindepth, &avgdepth, &leaves,
		    &perms, &nperms);
  notify_format(player, "%-10s %7d %7d %6d %4d %4d %9lu %11.3f %7lu",
		name, (int) root->count, leaves, mindepth, maxdepth,
		avgdepth, perms,
		((double) nperms / (double) (root->count - perms)), bytes);
}

/* Tree rotations.  These preserve left-to-right ordering,
 * while modifying depth.
 */
static void
st_left_rotate(int tree_depth, StrNode **root)
{
  StrNode *x;
  StrNode *y;

  x = path[tree_depth];
  assert(x);
  y = x->right;
  assert(y);
  x->right = y->left;
  y->left = x;
  if (*root == x)
    *root = y;
  else if (path[tree_depth - 1]->left == x)
    path[tree_depth - 1]->left = y;
  else
    path[tree_depth - 1]->right = y;
}

static void
st_right_rotate(int tree_depth, StrNode **root)
{
  StrNode *y;
  StrNode *x;

  y = path[tree_depth];
  assert(y);
  x = y->left;
  assert(x);
  y->left = x->right;
  x->right = y;
  if (*root == y)
    *root = x;
  else if (path[tree_depth - 1]->right == y)
    path[tree_depth - 1]->right = x;
  else
    path[tree_depth - 1]->left = x;
}

/** String tree insert.  If the string is already in the tree, bump its usage
 * count and return the tree's version.  Otherwise, allocate a new tree
 * node, copy the string into the node, insert it into the tree, and
 * return the new node's string.
 * \param s string to insert in tree.
 * \param root pointer to root of string tree.
 * \return string inserted or NULL.
 */
char const *
st_insert(char const *s, StrTree *root)
{
  int tree_depth;
  StrNode *n;
  int cmp = 0;
  size_t keylen;

  assert(s);

  /* Hunt for the string in the tree. */
  tree_depth = 0;
  n = root->root;
  while (n && (cmp = strcmp(s, n->string))) {
    path[tree_depth] = n;
    tree_depth++;
    assert(tree_depth < ST_MAX_DEPTH);
    if (cmp < 0)
      n = n->left;
    else
      n = n->right;
  }

  if (n) {
    /* Found the string, so bump the usage and return. */
    if (n->info < ST_USE_LIMIT)
      n->info += ST_USE_STEP;
    return n->string;
  }

  /* Need a new node.  Allocate and initialize it. */
  keylen = strlen(s) + 1;
  n = mush_malloc(sizeof(StrNode) - BUFFER_LEN + keylen, "StrNode");
  if (!n)
    return NULL;
  memcpy(n->string, s, keylen);
  n->left = NULL;
  n->right = NULL;
  if (tree_depth == 0) {
    /* This is the first insertion!  Just stick it at the root
     * and get out of here. */
    root->root = n;
    n->info = ST_BLACK + ST_USE_STEP;
    return n->string;
  }
  n->info = ST_RED + ST_USE_STEP;

  /* Foo.  Have to do a complex insert.  Well, start by putting
   * the new node at the tip of an appropriate branch. */
  path[tree_depth] = n;
  tree_depth--;
  if (cmp < 0)
    path[tree_depth]->left = n;
  else
    path[tree_depth]->right = n;

  /* I rely on ST_RED != 0 and ST_BLACK == 0 in my bitwise ops. */
  assert(ST_RED);

  /* Sigh.  Fix the tree to maintain the red-black properties. */
  while (tree_depth > 0 && (path[tree_depth]->info & ST_COLOR) == ST_RED) {
    /* We have a double-red.  Blitch.  Now we have some mirrored
     * cases to look for, so stuff is duplicated left/right. */
    if (path[tree_depth] == path[tree_depth - 1]->left) {
      StrNode *y;
      y = path[tree_depth - 1]->right;
      if (y && (y->info & ST_COLOR) == ST_RED) {
	/* Hmph.  Uncle is red.  Push the mess up the tree. */
	path[tree_depth]->info &= ~ST_RED;
	y->info &= ~ST_RED;
	tree_depth--;
	path[tree_depth]->info |= ST_RED;
	tree_depth--;
      } else {
	/* Okay, uncle is black.  We can fix everything, now. */
	if (path[tree_depth + 1] == path[tree_depth]->right) {
	  st_left_rotate(tree_depth, &root->root);
	  path[tree_depth + 1]->info &= ~ST_RED;
	} else {
	  path[tree_depth]->info &= ~ST_RED;
	}
	path[tree_depth - 1]->info |= ST_RED;
	st_right_rotate(tree_depth - 1, &root->root);
	break;
      }
    } else {
      StrNode *y;
      y = path[tree_depth - 1]->left;
      if (y && (y->info & ST_COLOR) == ST_RED) {
	/* Hmph.  Uncle is red.  Push the mess up the tree. */
	path[tree_depth]->info &= ~ST_RED;
	y->info &= ~ST_RED;
	tree_depth--;
	path[tree_depth]->info |= ST_RED;
	tree_depth--;
      } else {
	/* Okay, uncle is black.  We can fix everything, now. */
	if (path[tree_depth + 1] == path[tree_depth]->left) {
	  st_right_rotate(tree_depth, &root->root);
	  path[tree_depth + 1]->info &= ~ST_RED;
	} else {
	  path[tree_depth]->info &= ~ST_RED;
	}
	path[tree_depth - 1]->info |= ST_RED;
	st_left_rotate(tree_depth - 1, &root->root);
	break;
      }
    }
  }

  /* The tree is now red-black true again.  Make the root black
   * just for convenience. */
  root->root->info &= ~ST_RED;
  root->count++;
  root->mem += strlen(s) + 1;
  return n->string;
}

/** Tree find.  Basically the first part of insert. 
 * \param s string to find.
 * \param root pointer to root of string tree.
 * \return string if found, or NULL.
 */
char const *
st_find(char const *s, StrTree *root)
{
  int tree_depth;
  StrNode *n;
  int cmp;

  assert(s);

  /* Hunt for the string in the tree. */
  tree_depth = 0;
  n = root->root;
  while (n && (cmp = strcmp(s, n->string))) {
    path[tree_depth] = n;
    tree_depth++;
    assert(tree_depth < ST_MAX_DEPTH);
    if (cmp < 0)
      n = n->left;
    else
      n = n->right;
  }

  if (n)
    return n->string;
  return NULL;
}

/** Tree delete.  Decrement the usage count of the string, unless the
 * count is pegged.  If count reaches zero, delete.
 * \param s string to find and delete.
 * \param root pointer to root of string tree.
 */
void
st_delete(char const *s, StrTree *root)
{
  int tree_depth;
  StrNode *y;
  StrNode *x;
  int cmp;

  assert(s);

  /* Hunt for the string in the tree. */
  tree_depth = 0;
  y = root->root;
  while (y && (cmp = strcmp(s, y->string))) {
    path[tree_depth] = y;
    tree_depth++;
    assert(tree_depth < ST_MAX_DEPTH);
    if (cmp < 0)
      y = y->left;
    else
      y = y->right;
  }

  /* If it wasn't in the tree, we're done. */
  if (!y)
    return;

  /* If this node is permanent, then we're done. */
  if (y->info >= ST_USE_LIMIT)
    return;

  /* If this node has been used more than once, then decrement and exit. */
  if (y->info >= ST_USE_STEP * 2) {
    y->info -= ST_USE_STEP;
    return;
  }
  if (y->left && y->right) {
    /* It has two children.  We need to swap with successor. */
    int z_depth;
    char color;
    /* Record where we are. */
    z_depth = tree_depth;
    /* Find the successor. */
    path[tree_depth] = y;
    tree_depth++;
    y = y->right;
    while (y->left) {
      path[tree_depth] = y;
      tree_depth++;
      y = y->left;
    }
    /* Fix the parent's pointer... */
    if (z_depth == 0)
      root->root = y;
    else if (path[z_depth - 1]->left == path[z_depth])
      path[z_depth - 1]->left = y;
    else
      path[z_depth - 1]->right = y;
    /* Swap out the path pieces. */
    path[tree_depth] = path[z_depth];
    path[z_depth] = y;
    y = path[tree_depth];
    /* Swap out the child pointers */
    path[z_depth]->left = y->left;
    y->left = NULL;
    y->right = path[z_depth]->right;
    path[z_depth]->right = path[z_depth + 1];
    /* Fix the child pointer of the parent of the replacement */
    if (tree_depth > z_depth + 1)
      path[tree_depth - 1]->left = y;
    else
      path[tree_depth - 1]->right = y;
    /* Swap out the color */
    color = y->info & ST_COLOR;
    y->info = (y->info & ~ST_COLOR) | (path[z_depth]->info & ST_COLOR);
    path[z_depth]->info = (path[z_depth]->info & ~ST_COLOR) | color;
  }

  /* We are now looking at a node with less than two children */
  assert(!y->left || !y->right);
  /* Move the child (if any) up */
  if (y->left)
    x = y->left;
  else
    x = y->right;
  if (root->root == y)
    root->root = x;
  else if (path[tree_depth - 1]->left == y)
    path[tree_depth - 1]->left = x;
  else
    path[tree_depth - 1]->right = x;
  if ((y->info & ST_COLOR) == ST_BLACK) {
    while (x != root->root && (!x || (x->info & ST_COLOR) == ST_BLACK)) {
      if (x == path[tree_depth - 1]->left) {
	StrNode *w = path[tree_depth - 1]->right;
	assert(w);
	if (w && (w->info & ST_COLOR) == ST_RED) {
	  w->info &= ~ST_RED;
	  path[tree_depth - 1]->info |= ST_RED;
	  st_left_rotate(tree_depth - 1, &root->root);
	  path[tree_depth] = path[tree_depth - 1];
	  path[tree_depth - 1] = w;
	  tree_depth++;
	  w = path[tree_depth - 1]->right;
	  assert(w);
	}
	assert((w->info & ST_COLOR) == ST_BLACK);
	if ((!w->left || (w->left->info & ST_COLOR) == ST_BLACK) &&
	    (!w->right || (w->right->info & ST_COLOR) == ST_BLACK)) {
	  w->info |= ST_RED;
	  x = path[tree_depth - 1];
	  tree_depth--;
	} else {
	  if (!w->right || (w->right->info & ST_COLOR) == ST_BLACK) {
	    assert(w->left);
	    w->left->info &= ~ST_RED;
	    path[tree_depth] = w;
	    st_right_rotate(tree_depth, &root->root);
	    w = path[tree_depth - 1]->right;
	    assert(w);
	  }
	  w->info =
	    (w->info & ~ST_COLOR) | (path[tree_depth - 1]->info & ST_COLOR);
	  path[tree_depth - 1]->info &= ~ST_RED;
	  assert(w->right);
	  w->right->info &= ~ST_RED;
	  st_left_rotate(tree_depth - 1, &root->root);
	  x = root->root;
	}
      } else {
	StrNode *w = path[tree_depth - 1]->left;
	assert(w);
	if (w && (w->info & ST_COLOR) == ST_RED) {
	  w->info &= ~ST_RED;
	  path[tree_depth - 1]->info |= ST_RED;
	  st_right_rotate(tree_depth - 1, &root->root);
	  path[tree_depth] = path[tree_depth - 1];
	  path[tree_depth - 1] = w;
	  tree_depth++;
	  w = path[tree_depth - 1]->left;
	  assert(w);
	}
	assert((w->info & ST_COLOR) == ST_BLACK);
	if ((!w->right || (w->right->info & ST_COLOR) == ST_BLACK) &&
	    (!w->left || (w->left->info & ST_COLOR) == ST_BLACK)) {
	  w->info |= ST_RED;
	  x = path[tree_depth - 1];
	  tree_depth--;
	} else {
	  if (!w->left || (w->left->info & ST_COLOR) == ST_BLACK) {
	    assert(w->right);
	    w->right->info &= ~ST_RED;
	    path[tree_depth] = w;
	    st_left_rotate(tree_depth, &root->root);
	    w = path[tree_depth - 1]->left;
	    assert(w);
	  }
	  w->info =
	    (w->info & ~ST_COLOR) | (path[tree_depth - 1]->info & ST_COLOR);
	  path[tree_depth - 1]->info &= ~ST_RED;
	  assert(w->left);
	  w->left->info &= ~ST_RED;
	  st_right_rotate(tree_depth - 1, &root->root);
	  x = root->root;
	}
      }
    }
    if (x)
      x->info &= ~ST_RED;
  }
  root->mem -= strlen(s) + 1;
  mush_free(y, "StrNode");
  root->count--;
}

/* Print the tree, for debugging purposes. */
static void
st_print_tree(StrNode *node, int tree_depth, int lead)
{
  static char leader[ST_MAX_DEPTH * 2 + 1];
  char tmp;
  static StrNode *print_path[ST_MAX_DEPTH];
  int looped;

  if (tree_depth == 0)
    memset(leader, ' ', sizeof leader);

  print_path[tree_depth] = node;
  looped = tree_depth;
  while (looped--)
    if (print_path[looped] == node)
      break;
  looped++;

  if (node->left && !looped)
    st_print_tree(node->left, tree_depth + 1, '.');
  tmp = leader[tree_depth * 2];
  leader[tree_depth * 2] = '\0';
  printf("%s%c-+ %c %d %s%s\n", leader, lead,
	 (node->info & ST_COLOR) ? 'r' : 'b', node->info / ST_USE_STEP,
	 node->string, looped ? " -LOOPING" : "");
  leader[tree_depth * 2] = ' ' + '|' - tmp;
  leader[0] = ' ';
  if (node->right && !looped)
    st_print_tree(node->right, tree_depth + 1, '`');
}

/** Print a string tree (for debugging).
 * \param root pointer to root of string tree.
 */
void
st_print(StrTree *root)
{
  printf("---- print\n");
  if (root->root)
    st_print_tree(root->root, 0, '-');
  printf("----\n");
}

static void st_depth_helper
  (StrNode *node, int *maxdepth, int *mindepth, int *avgdepth, int *leaves,
   unsigned long *perms, unsigned long *nperms, int count);
static void
st_depth_helper(StrNode *node, int *maxdepth, int *mindepth,
		int *avgdepth, int *leaves, unsigned long *perms,
		unsigned long *nperms, int count)
{
  if (!node)
    return;

  if (node->info >= ST_USE_LIMIT)
    (*perms)++;
  else
    (*nperms) += node->info;

  if (count > *maxdepth)
    *maxdepth = count;

  if (node->left) {
    /* Inner node */
    st_depth_helper(node->left, maxdepth, mindepth, avgdepth, leaves, perms,
		    nperms, count + 1);
  }
  if (node->right) {
    /* Inner node */
    st_depth_helper(node->right, maxdepth, mindepth, avgdepth, leaves, perms,
		    nperms, count + 1);
  }
  if (!node->left && !node->right) {	/* This is a leaf node */
    (*leaves)++;
    (*avgdepth) += count;
    if (*mindepth > count)
      *mindepth = count;
  }
}


/* Find the depth and number of permanment nodes */
static void
st_traverse_stats(StrNode *node, int *maxdepth, int *mindepth, int *avgdepth,
		  int *leaves, unsigned long *perms, unsigned long *nperms)
{
  *maxdepth = 0;
  *mindepth = node ? (ST_MAX_DEPTH + 1) : 0;
  *perms = 0;
  *nperms = 0;
  *avgdepth = 0;
  *leaves = 0;
  st_depth_helper(node, maxdepth, mindepth, avgdepth, leaves, perms, nperms, 1);
  if (*avgdepth)
    *avgdepth = *avgdepth / *leaves;
}