#ifndef BST_H_
#define BST_H_

// Oldham, Jeffrey D.
// 2000Apr09
// CS1321

// Modified by:
// Massingill, Berna L.
// 2001Apr

// Functions for Binary Search Trees

// These binary search tree routines are defined recursively since we
// are using recursively defined trees.  They are template functions
// since the Tree class is a template class.

// We assume that "<" is defined for ItemType.

#include <iostream>
#include <stdlib.h>		// has rand()
#include <assert.h>
#include "tree.h"

// Function to insert an item.
// Pre:  t is a BST.
// Post:  returns a BST containing i and all elements of t.
//          (If t contains i already, the returned tree is
//          just t; otherwise it's t with i inserted.)
template <class ItemType>
Tree<ItemType> insert(const ItemType & i, const Tree<ItemType> & t) {
  if (t.empty())
    return Tree<ItemType>(i, Tree<ItemType>(), Tree<ItemType>());
  else {			// nonempty tree
    if (i < t.item())
      return Tree<ItemType>(t.item(), insert(i, t.left()), t.right());
    else if (t.item() < i)
      return Tree<ItemType>(t.item(), t.left(), insert(i, t.right()));
    else			// i == t.item() so already in the tree
      return t;
  }
}


// Function to search for an item.
// Pre:  t is a BST.
// Post:  returns true if i is in t, false otherwise.
template <class ItemType>
bool query(const ItemType & i, const Tree<ItemType> & t) {
  if (t.empty())
    return false;
  else {			// nonempty tree
    if (i < t.item())
      return query(i, t.left());
    else if (t.item() < i)
      return query(i, t.right());
    else			// i == t.item()
      return true;
  }
}


// << operator:  print the tree's contents.
// Post:  all elements of t have been printed to "out", 
//          in inorder-traversal order, one per line.
template <class ItemType>
ostream& operator<<(ostream & out, const Tree<ItemType> & t) {
  if (t.empty())
    return out;
  else {			// nonempty tree
    return out << t.left() << t.item() << endl << t.right();
  }
}


// Functions for removing items:

// Helper function to remove greatest element of BST.
// Pre:  t is a BST; t is not empty.
// Post:  "max" is the greatest element in t.
//        returns a BST containing all elements of t
//          except "max".
template <class ItemType>
Tree<ItemType> removeMax(const Tree<ItemType> & t, ItemType &max) {
  assert(!t.empty());
  if (t.right().empty()) {	// no right subtree
    max = t.item();
    return t.left();
  }
  else				// nonempty right subtree
    return Tree<ItemType>(t.item(), t.left(), removeMax(t.right(), max));
}

// Helper function to remove least element of BST.
// Pre:  t is a BST; t is not empty.
// Post:  "min" is the smallest element in t.
//        returns a BST containing all elements of t
//          except "min".
template <class ItemType>
Tree<ItemType> removeMin(const Tree<ItemType> & t, ItemType &min) {
  assert(!t.empty());
  if (t.left().empty()) {	// no left subtree
    min = t.item();
    return t.right();
  }
  else				// nonempty left subtree
    return Tree<ItemType>(t.item(), removeMin(t.left(), min), t.right());
}

// Function to remove an item.
// Pre:  t is a BST.
// Post:  returns a BST containing all elements of t except i.
//          (If t does not contain i, the returned tree is
//          just t; otherwise it's t with i removed.)
template <class ItemType>
Tree<ItemType> remove(const ItemType & i, const Tree<ItemType> & t) {
  if (t.empty())
    return t;
  else {			// nonempty tree
    if (i < t.item())
      return Tree<ItemType>(t.item(), remove(i, t.left()), t.right());
    else if (t.item() < i)
      return Tree<ItemType>(t.item(), t.left(), remove(i, t.right()));
    else {			// i == t.item()
      if (t.left().empty())
	return t.right();
      else if (t.right().empty())
	return t.left();
      else {			// neither subtree empty; "flip a
				// coin" to decide which to change.
	if (rand() / static_cast<double>(RAND_MAX) * 2 >= 1) {
	  ItemType itm;
	  Tree<ItemType> tr = removeMax(t.left(), itm);
	  return Tree<ItemType>(itm, tr, t.right());
	}
	else {
	  ItemType itm;
	  Tree<ItemType> tr = removeMin(t.right(), itm);
	  return Tree<ItemType>(itm, t.left(), tr);
	}
      }
    }
  }
}

#endif // BST_H_