// FILE: bag.cpp
// -------------------------------------------------------------------------
// This is a partial implementation of the bag template class. The parts marked
// with ***STUDENT WORK*** are left as a project for data structures
// students. 
// -------------------------------------------------------------------------
// TEMPLATE CLASS IMPLEMENTED: bag<ItemType> (see bag.h for documentation)
// INVARIANT of the ADT:
//   root_ptr is the root pointer of a binary search tree that contains the
//   bag's items.
//

#include <cassert>
#include <cstdlib>
namespace main_savitch_10
{
    template <class ItemType>
    void bst_remove_max(binary_tree_node<ItemType>*& root_ptr, ItemType& removed)
	// Precondition: root_ptr is a root pointer of a non-empty binary 
	// search tree.
	// Postcondition: The largest item in the binary search tree has been
	// removed, and root_ptr now points to the root of the new (smaller) 
	// binary search tree. The reference parameter, removed, has been set 
	// to a copy of the removed item.
	{
	    /***STUDENT WORK***
	     ** This recursive function should be implemented by the student.
	     ** The base case occurs when there is no right child of the
	     ** root_ptr node. In this case, the root_ptr should be moved down
	     ** to its left child and then the original root node must be
	     ** deleted. There is also a recursive case, when the root does
	     ** have a right child. In this case, a recursive call can be made
	     ** using root_ptr->right( ) as the first parameter. Please note
	     ** in bintree.h, the return value from root_ptr->right( ); 
	     ** the right() function has the prototype:
	     **    binary_tree_node<ItemType>*&
	     ** The & symbol in the return type means that the return value is
	     ** a reference to the actual right pointer in the node. Any changes
	     ** made to this pointer in the recursive call will directly change
	     ** the right pointer in the root_ptr's node.
	     */
	}

    template <class ItemType>
	bool bst_remove(binary_tree_node<ItemType>*& root_ptr, const ItemType& target)
	// Precondition: root_ptr is a root pointer of a binary search tree 
	// or may be NULL for an empty tree).
	// Postcondition: If target was in the tree, then one copy of target
	// has been removed, and root_ptr now points to the root of the new 
	// (smaller) binary search tree. In this case the function returns true.
	// If target was not in the tree, then the tree is unchanged (and the
	// function returns false).
	{
	    binary_tree_node<ItemType> *oldroot_ptr;
	    
	    if (root_ptr == NULL)
	    {   // Empty tree
		return false;
	    }

	    if (target < root_ptr->data( ))
	    {   // Continue looking in the left subtree
		// Note: Any change made to root_ptr->left by this recursive
		// call will change the actual left pointer (because the return
		// value from the left() member function is a reference to the
		// actual left pointer.
		return bst_remove(root_ptr->left( ), target);
	    }

	    if (target > root_ptr->data( ))
	    {   // Continue looking in the right subtree
		// Note: Any change made to root_ptr->right by this recursive
		// call will change the actual right pointer (because the return
		// value from the right() member function is a reference to the
		// actual right pointer.
		return bst_remove(root_ptr->right( ), target);
	    }
	    
	    if (root_ptr->left( ) == NULL)
	    {   // Target was found and there is no left subtree, so we can
		// remove this node, making the right child be the new root.
		oldroot_ptr = root_ptr;
		root_ptr = root_ptr->right( );
		delete oldroot_ptr;
		return true;
	    }
	    
	    // If code reaches this point, then we must remove the target from
	    // the current node. We'll actually replace this target with the
	    // maximum item in our left subtree.
	    // Note: Any change made to root_ptr->left by bst_remove_max
	    // will change the actual left pointer (because the return
	    // value from the left() member function is a reference to the
	    // actual left pointer.
	    bst_remove_max(root_ptr->left( ), root_ptr->data( ));
	    return true;
	}

    template <class ItemType>
	int bst_remove_all
	(binary_tree_node<ItemType>*& root_ptr, const ItemType& target)
	// Precondition: root_ptr is a root pointer of a binary search tree 
	// or may be NULL for an empty tree).
	// Postcondition: All copies of target have been removed from the tree
	// has been removed, and root_ptr now points to the root of the new 
	// (smaller) binary search tree. The return value tells how many copies
	// of the target were removed.
	{
	    /** STUDENT WORK
	     ** Note: This implementation is similar to bst_remove, except that
	     ** all occurrences of the target must be removed, and the return
	     ** value is the number of copies that were removed.
	     */
	    
	    binary_tree_node<ItemType> *oldroot_ptr;
	    
	    if (root_ptr == NULL)
	    {   // Empty tree
	        /* STUDENT WORK */
	    }

	    if (target < root_ptr->data( ))
	    {   // Continue looking in the left subtree
	        /* STUDENT WORK */
	    }

	    if (target > root_ptr->data( ))
	    {   // Continue looking in the right subtree
	        /* STUDENT WORK */
	    }
	    
	    if (root_ptr->left( ) == NULL)
	    {   // Target was found and there is no left subtree, so we can
		// remove this node, making the right child be the new root.
		oldroot_ptr = root_ptr;
		root_ptr = root_ptr->right( );
		delete oldroot_ptr;
		return 1;
	    }
	    
	    // If code reaches this point, then we must remove the target from
	    // the current node. We'll actually replace this target with the
	    // maximum item in our left subtree. We then continue
	    // searching for more copies of the target to remove.
	    // This continued search must start at the current root (since
	    // the maximum element that we moved up from our left subtree
	    // might also be a copy of the target).
	    /* STUDENT WORK */

	}

    template <class ItemType>
	bag<ItemType>::bag(const bag<ItemType>& source)
	// Library facilities used: bintree.h
	{
	    root_ptr = tree_copy(source.root_ptr);
	}

    template <class ItemType>
	bag<ItemType>::~bag( )
	// Header file used: bintree.h
	{
	    tree_clear(root_ptr);
	}

    template <class ItemType>
	int bag<ItemType>::size( ) const
	// Header file used: bintree.h
	{
	    return tree_size(root_ptr);
	}

    template <class ItemType>
	void bag<ItemType>::insert(const ItemType& entry)
	// Header file used: bintree.h
	{
	    binary_tree_node<ItemType> *cursor;
	
	    if (root_ptr == NULL)
	    {   // Add the first node of the binary search tree:
		root_ptr = new binary_tree_node<ItemType>(entry);
		return;
	    }

	    else
	    {   // Move down the tree and add a new leaf:
		cursor = root_ptr;
		/* STUDENT WORK */
	    }
	}

    template <class ItemType>
	int bag<ItemType>::count(const ItemType& target) const
	{
	    int answer = 0;
	    binary_tree_node<ItemType> *cursor;

	    cursor = root_ptr;
	    /* STUDENT WORK */

	    return answer;
	}

    template <class ItemType>
	int bag<ItemType>::erase(const ItemType& target)
	{
	    return bst_remove_all(root_ptr, target);
	}

    template <class ItemType>
	bool bag<ItemType>::erase_one(const ItemType& target)
	{
	    return bst_remove(root_ptr, target);
	}

    template <class ItemType>
	void bag<ItemType>::operator =(const bag<ItemType>& source)
        // Header file used: bintree.h
        {
	    if (root_ptr == source.root_ptr)
	        return;
	    tree_clear(root_ptr);
	    root_ptr = tree_copy(source.root_ptr);
	}

    template <class ItemType>
	void bag<ItemType>::operator +=(const bag<ItemType>& addend)
        {
	    if (root_ptr == addend.root_ptr)
	    {
		bag<ItemType> copy = addend;
		insert_all(copy.root_ptr);
	    }
	    else
	        insert_all(addend.root_ptr);
	}

    template <class ItemType>
	bag<ItemType> operator +(const bag<ItemType>& b1, const bag<ItemType>& b2)
	{
	    bag<ItemType> answer = b1;
	    answer += b2;
	    return answer;
	}

    template <class ItemType>
	void bag<ItemType>::insert_all(binary_tree_node<ItemType>* addroot_ptr)
        // Precondition: addroot_ptr is the root pointer of a binary search tree that
        // is separate from the binary search tree of the bag that activated this
        // method.
        // Postcondition: All the items from the addroot_ptr's binary search tree
        // have been added to the binary search tree of the bag that activated this
        // method.
	{
	    if (addroot_ptr != NULL)
	    {
		insert(addroot_ptr->data( ));
		insert_all(addroot_ptr->left( ));
		insert_all(addroot_ptr->right( ));
	    }
	}
}
