/* Copyright (c) 1991-2002 Doshita Lab. Speech Group, Kyoto University */
/* Copyright (c) 2000-2002 Speech and Acoustics Processing Lab., NAIST */
/*   All rights reserved   */

/* ptree.c --- make patricia tree for given string array */
/*             data type: integer */

/* $Id: ptree.c,v 1.4 2002/09/11 22:01:50 ri Exp $ */

#include <sent/stddefs.h>
#include <sent/ptree.h>

/* string bit test function */
int
testbit(char *str, int bitplace)
{
  int maskptr;
  int bitshift;
  unsigned char maskbit;
  
  maskptr = bitplace / 8;
  if ((bitshift = bitplace % 8) != 0) {
    maskbit = 0x80 >> bitshift;
  } else {
    maskbit = 0x80;
  }

  return(str[maskptr] & maskbit);
}

/* find in which bit the two strings differ */
int
where_the_bit_differ(char *str1, char *str2)
{
  int p = 0;
  int bitloc;

  /* step: char, bit */
  while(str1[p] == str2[p]) p++;
  bitloc = p * 8;
  while(testbit(str1, bitloc) == testbit(str2, bitloc)) bitloc++;

  return(bitloc);
}


/* malloc and return a new node */
static PATNODE *
new_node()
{
  PATNODE *tmp;

  tmp = (PATNODE *)mymalloc(sizeof(PATNODE));
  tmp->left0 = NULL;
  tmp->right1 = NULL;

  return(tmp);
}

/* make a patricia tree for given string arrays */
PATNODE *
make_ptree(char **words, int *data, int wordsnum, int bitplace)
{
  int i,j, tmp;
  char *p;
  int newnum;
  PATNODE *ntmp;

#if 0
  j_printf("%d:", wordsnum);
  for (i=0;i<wordsnum;i++) {
    j_printf(" %s",words[i]);
  }
  j_printf("\n");
  j_printf("test bit = %d\n", bitplace);
#endif

  if (wordsnum == 1) {
    /* word identified: this is leaf node */
    ntmp = new_node();
    ntmp->value.data = data[0];
    return(ntmp);
  }

  newnum = 0;
  for (i=0;i<wordsnum;i++) {
    if (testbit(words[i], bitplace) != 0) {
      newnum++;
    }
  }
  if (newnum == 0 || newnum == wordsnum) {
    /* all words has same bit, continue to descend */
    return(make_ptree(words, data, wordsnum, bitplace + 1));
  } else {
    /* sort word pointers by tested bit */
    j = wordsnum-1;
    for (i=0; i<newnum; i++) {
      if (testbit(words[i], bitplace) == 0) {
	for (; j>=newnum; j--) {
	  if (testbit(words[j], bitplace) != 0) {
	    p = words[i]; words[i] = words[j]; words[j] = p;
	    tmp = data[i]; data[i] = data[j]; data[j] = tmp;
	    break;
	  }
	}
      }
    }
    /* create node and descend for each node */
    ntmp = new_node();
    ntmp->value.thres_bit = bitplace;
    ntmp->right1 = make_ptree(words, data, newnum, bitplace+1);
    ntmp->left0  = make_ptree(&(words[newnum]), &(data[newnum]), wordsnum-newnum, bitplace+1);
    return(ntmp);
  }
}


/* display tree structure (for DEBUG) */
/* traverse pre-order */
void
disp_ptree(PATNODE *node, int level)
{
  int i;

  for (i=0;i<level;i++) {
    j_printf("-");
  }
  if (node->left0 == NULL && node->right1 == NULL) {
    j_printf("LEAF:%d\n", node->value.data);
  } else {
    j_printf("%d\n", node->value.thres_bit);
    if (node->left0 != NULL) {
      disp_ptree(node->left0, level+1);
    }
    if (node->right1 != NULL) {
      disp_ptree(node->right1, level+1);
    }
  }
}

/* do search and return data (internal recursive) */
static int
ptree_search_data_r(char *str, PATNODE *node)
{
  if (node->left0 == NULL && node->right1 == NULL) {
    return(node->value.data);
  } else {
    if (testbit(str, node->value.thres_bit) != 0) {
      return(ptree_search_data_r(str, node->right1));
    } else {
      return(ptree_search_data_r(str, node->left0));
    }
  }
}

/* do search and return data */
int
ptree_search_data(char *str, PATNODE *node)
{
  if (node == NULL) {
    j_error("Error: ptree_search_data: no node, search for \"%s\" failed\n", str);
  }
  return(ptree_search_data_r(str, node));
}


/*******************************************************************/
/* add 1 node to given ptree */

/* first when root node does not exist... */
PATNODE *
ptree_make_root_node(int data)
{
  PATNODE *nnew;
  /* make new leaf node for newstr */
  nnew = new_node();
  nnew->value.data = data;
  return(nnew);
}

/* add node to the point */
static void
ptree_add_entry_at(char *str, int bitloc, int data, PATNODE **parentlink)
{
  PATNODE *node;
  node = *parentlink;
  if (node->value.thres_bit > bitloc ||
      (node->left0 == NULL && node->right1 == NULL)) {
    PATNODE *newleaf, *newbranch;
    /* insert between [parent] and [node] */
    newleaf = new_node();
    newleaf->value.data = data;
    newbranch = new_node();
    newbranch->value.thres_bit = bitloc;
    *parentlink = newbranch;
    if (testbit(str, bitloc) ==0) {
      newbranch->left0  = newleaf;
      newbranch->right1 = node;
    } else {
      newbranch->left0  = node;
      newbranch->right1 = newleaf;
    }
    return;
  } else {
    if (testbit(str, node->value.thres_bit) != 0) {
      ptree_add_entry_at(str, bitloc, data, &(node->right1));
    } else {
      ptree_add_entry_at(str, bitloc, data, &(node->left0));
    }
  }
}

/* top routine to add entry */
void
ptree_add_entry(char *str, int data, char *matchstr, PATNODE **rootnode)
{
  int bitloc;

  bitloc = where_the_bit_differ(str, matchstr);
  if (*rootnode == NULL) {
    *rootnode = ptree_make_root_node(data);
  } else {
    ptree_add_entry_at(str, bitloc, data, rootnode);
  }

}

/*** free all the sub nodes from specified node ****/
void
free_ptree(PATNODE *node)
{
  if (node->left0 != NULL) free_ptree(node->left0);
  if (node->right1 != NULL) free_ptree(node->right1);
  free(node);
}
