// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: tree.cpp
// Compiler Used: MSVC40, DJGPP 2.7.2.1, GCC 2.7.2.1, HP CPP 10.24
// Produced by: Doug Gaer
// File Creation Date: 11/07/1996
// Date Last Modified: 07/30/1998
// ----------------------------------------------------------- // 
// ------------- Program description and details ------------- // 
// ----------------------------------------------------------- // 
/*
THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
CORRECTION.

This is a simple class implementation of a binary search tree
used to create a tree of character strings. The char * data type
in the TreeNode class is used as a pointer to a pre-allocated
strings. The tree structure itself stores a hierarchal collection
of nodes. Each node stores a pointer to a unique character string
along with left and right pointers to other nodes in the tree.
*/
// ----------------------------------------------------------- // 
#include <iostream.h>
#include <string.h>

class Tnode; // Forward class name

// VisitFunc is a pointer to void(Tnode *Node) that points to
// a visit function used to process node data during tree traversals
typedef void (*VisitFunc)(Tnode *Node); 

// (T)ree (N)ode base class
class Tnode
{
public:
  Tnode() { right = left = 0; data = 0; }
  Tnode(char *key, Tnode *l = 0, Tnode *r = 0) {
    data = strdup(key); left = l; right = r; 
  }
  
public:
  char *data;   // data for this tree 
  Tnode *right; // subtree to the right 
  Tnode *left;  // subtree to the left 
};

// Binary Search (T)ree class
class Tree : public Tnode
{
public:
  Tree() { root = 0; }
  ~Tree() { Clear(); }
  Tree(const Tree &t) { this->Copy(t); }
  void operator=(const Tree &t) { this->Copy(t); }
  
public:
  int Add(char *key);
  int Remove(char *key);
  int Find(char *key);
  void Clear();
  int Copy(const Tree &t);
  void PrintTree(VisitFunc Visit) { PrintInOrder(root, Visit); }
  int GetHeight() { return Height(root); }
  
private: // Functions for internal processing only
  Tnode *SearchForParent(Tnode *node, char *key, Tnode *&parent, int &side);
  Tnode *Insert(Tnode *&root, char *key, int &exists);
  Tnode *DetachNode(Tnode *&root, Tnode *node, Tnode *parent, int side);
  Tnode *GetParentOfSuccessor(Tnode *node);
  Tnode *Detach(Tnode *&root, char *key);
  int Delete(Tnode *&root, char *key);
  void PrintInOrder(Tnode *tree, VisitFunc Visit);
  void DeleteTree(Tnode *t);
  Tnode *DupTree(Tnode *t);
  int Height(Tnode *t);
  
private:
  Tnode *root; // Pointer to the top of the tree 
};

int Tree::Add(char *key)
// Add an entry to the tree. 
{
  int exists;
  Insert(root, key, exists);
  return exists == 0; // Return true if the key did not already exist
}

int Tree::Remove(char *key)
// Remove a node from the tree.
// Returns zero if the node does not exist.
{
  return Delete(root, key);
}

void Tree::Clear()
// Clear the tree.
{
  DeleteTree(root);
  root = 0;
}

int Tree::Find(char *key)
// Search the tree for matching entry 
{
  Tnode *parent;
  int side;

  // Search the tree
  Tnode *node = SearchForParent(root, key, parent, side);
 
  return node != 0; // Return true if the matching node is found
}

int Tree::Copy(const Tree &t)
// Copy tree t into the tree object that invoked the call. 
{
  Clear();
  Tnode *r = DupTree(t.root);

  if(r) {
    root = r; // Tree was copied from the bottom up
    return 1;
  }
  return 0; // Tree was not copied
}

Tnode *Tree::Insert(Tnode *&root, char *key, int &exists)
// Insert a new node into the tree. Returns the matching node,
// new or old and may modify the root pointer.
{
  Tnode *parent;
  int side;

  // Look for a place to insert the node
  Tnode *node = SearchForParent(root, key, parent, side);

  if(node == 0) { // Node does not exist in the tree
    node = new Tnode(key);
    if(parent) {
      if(side) parent->right = node; else parent->left = node;
    }
    else // No parent, so this is the new root
      root = node;
    
    exists = 0;
  }
  else
    exists = 1;

  return node;
}

Tnode *Tree::SearchForParent(Tnode *node, char *key, Tnode *&parent, int &side)
// Returns the parent of the matching node, as well as the node itself. 
// Each node to the left is smaller then the given node, and each node
// to right is larger.
{
  parent = 0;
  while(node) {
    if(strcmp(key, node->data) == 0) break;
    parent = node;
    if(strcmp(key, node->data) < 0) { // Node is smaller
      side = 0;
      node = node->left;
    }
    else { // Node is larger
      side = 1;
      node = node->right;
    }
  }
  return node;
}

Tnode *Tree::DetachNode(Tnode *&root, Tnode *node, Tnode *parent, int side)
// Detach the node with parent from the tree. The node is the left
// child if side equals zero or right child if side equals one.
// Returns a pointer to the node.
{
  Tnode *psucc; // Parent of successor
  Tnode *replacement;

  if(node) {
    if(node->left == 0 || node->right == 0) {
      // At lease one child is null, so use the other as the replacement
      replacement = (node->left) ? node->left : node->right;
    }
    else { // Neither child is null
      psucc = GetParentOfSuccessor(node); // Guaranteed not to be null
      if(psucc == node) { // Immediate successor
	replacement = psucc->right;
      }
      else {
	// Detach replacement from its current loaction and
        // relocate it to where node used to be
	replacement = psucc->left;
	psucc->left = psucc->left->right;
        replacement->right = node->right;
      }

      // Finish reloacting replacement to where node used to be
      replacement->left = node->left;
    }

    if(parent) { // Fix parent of node to point to replacement
      if(side)
        parent->right = replacement; else parent->left = replacement;
    }
    else root = replacement; // No parent, so node was the root
  }
  return node;
}

Tnode *Tree::GetParentOfSuccessor(Tnode *node)
// Used by the Tree::DetachNode() function to find the parent of
// the successor node. Returns zero if no successor is found.
{
  Tnode *parent = 0;

  // Go right once, then all the way to the left
  Tnode *q = node->right;
  if(q) {
    parent = node;
    while(q->left) {
      parent = q;
      q = q->left;
    }
  }
  return parent;
}

Tnode *Tree::Detach(Tnode *&root, char *key)
// Detaches the node matching key from the tree.
{
  int side;
  Tnode *parent, *node;
  node = SearchForParent(root, key, parent, side);
  return DetachNode(root, node, parent, side);
}

int Tree::Delete(Tnode *&root, char *key)
// Deletes the node from the tree. 
// Returns zero if the node does not exist.
{
  Tnode *node = Detach(root, key);
  delete node;
  return node != 0;
}

void Tree::DeleteTree(Tnode *t)
// Recursive function used to clear the tree from the bottom up.
{
  if(t == 0) return;
  if(t->left == 0) return; else DeleteTree(t->left);
  if(t->right == 0) return; else DeleteTree(t->right);
  delete t;
}

void Tree::PrintInOrder(Tnode *tree, VisitFunc Visit)
// Visit each node of tree Tree in in-order, (first the Left
// subtree, then the parent, then the Right subtree), with 
// tail recursion removed
{
  while(tree) {
    PrintInOrder(tree->left, Visit);
    Visit(tree);
    tree = tree->right;
  }
}

Tnode *Tree::DupTree(Tnode *t)
// Recursive function that to duplicates a tree using
// postorder traversal.
{
  if(t == 0) return 0;
  Tnode *l = DupTree(t->left);
  Tnode *r = DupTree(t->right);
  return new Tnode(t->data, l, r);
}

int Tree::Height(Tnode *t)
// The height of a tree is the maximum of the height of
// its two subtrees. Returns the hight of the tree.
{
  int h = 0;
  if(t != 0) {
    int lh = Height(t->left);
    int rh = Height(t->right);
    h = ((lh > rh) ? lh : rh) +1;
  }
  return h;
}

// Test program code starts here
// *********************************************************** //
char *InputData()
// Input a line of text from the console
{
  char buf[255];
  cout << "Input Data: ";
  cin.getline(buf, sizeof(buf));
  unsigned len = 0;
  for(unsigned i = 0; (buf[i] != ' ') && (buf[i] != '\0'); i++) len++;
  char *s = new char[len];
  s[len] = '\0';
  memmove(s, buf, len);
  return s;
}

void PrintNode(Tnode *node)
// Visit function used to print the node data to the console
{
  cout << node->data << endl;
}

void SkipToEol(istream &s)
// Used to clear istream
{
  char c;
  s.clear();
  while(s.get(c) && c != '\n') { ; }
}

int Quit()
// Terminate the program
{
  cout << "Exiting..." << endl;
  return 0;
}

void pause()
// Pause the program 
{
  cout << endl;
  cout << "Press enter to continue..." << endl;
  cin.get();
}

void Menu(void)
// List the program options
{
  cout << "(?)   Help (prints this menu)" << endl;
  cout << "(A)   Add an item to the tree" << endl;
  cout << "(B)   Build a tree of strings" << endl;
  cout << "(C)   Clear the tree" << endl;
  cout << "(D)   Delete an item from the tree" << endl;
  cout << "(F)   Find an item in the tree" << endl;
  cout << "(H)   Display the tree height" << endl;
  cout << "(L)   List tree items in order" << endl; 
  cout << "(Q)   Quit this program" << endl; 
}

// Create an array of words to load in the tree
const int Items = 5; // Adjust this number if any more items are added
char *TreeData[Items] = { "EASY", "COOK", "DELTA", "ABLE", "BAKER", };

int main()
// Test program's main thread of execution
{
  Menu(); // Display the options menu
  
  char key;
  char *key_buf;
  Tree tree;
  int i, rv = 1;

  while(rv) {
    if (!cin) { // Input is in fail state
       SkipToEol(cin); // Go to end of line
       if (!cin) {  // Can't fix
          cout << "Input stream is broken" << endl;
          return 0;
       }
    }
    cout << '>';
    cin >> key;
    if (!cin) continue; // Fix at top of loop
    switch(key) {
      case 'a' : case 'A' :
	SkipToEol(cin);
	key_buf = InputData();
	tree.Add(key_buf);
	break;

      case 'b' : case 'B' :
	SkipToEol(cin);
	  // Add the entries to the tree
	for(i = 0; i < Items; i++) {
	  // Exit loop if Items is greater then the number of array elements  
	  if(TreeData[i] == 0) break; // 0 means the array is empty
	  tree.Add(TreeData[i]);
	}
	break;

      case 'c' : case 'C' :
	SkipToEol(cin);
	tree.Clear();
	break;

      case 'd' : case 'D' :
	SkipToEol(cin);
	key_buf = InputData();
	if(tree.Remove(key_buf)) {
	  cout << "Removed " << key_buf << endl;
	}
	else {
	  cout << key_buf << " does not exists in the tree" << endl;
	}
	break;

      case 'f' : case 'F' :
	SkipToEol(cin);
	key_buf = InputData();
	if(tree.Find(key_buf)) {
	  cout << "Found entry " << key_buf << endl;
	}
	else {
	  cout << key_buf << " does not exists in the tree" << endl;
	}
	break;	

      case 'l' : case 'L' :
	SkipToEol(cin);
	tree.PrintTree(PrintNode);
	break;	 

      case 'h' : case 'H' :
	SkipToEol(cin);
	cout << "Height = " << tree.GetHeight() << endl;
	break;

      case 'q' : case 'Q' :
	rv = Quit();
	break;

      case '?' : 
	SkipToEol(cin);
	Menu();
	break;

      default:
        cout << "Unrecognized command" << endl;
    }
  }
  return 0;
}
// ----------------------------------------------------------- // 
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //
