// ---------------------------------------------------------------------------
// SimpleUnboundedStack.h
//
// A generic unbounded stack class.  (There is already a stack class in the 
// Standard C++ Library; this class is just an illustration of how such a
// class is written from scratch!)  Copying and assignment are prohibited 
// because this is just a simple example.  A real class would have copy
// constructors and an assignment operator defined.
//
//   Stack()       Constructor: declares a stack and initializes it to empty.
//   ~Stack()      Destructor.
//   s.size()      Returns the number of elements in the stack.
//   s.isEmpty()   Returns whether the stack is empty.
//   s.push(x)     Pushes x.   Will throw a bad_alloc if there is no memory
//                 available to add the element (but note that many compilers
//                 do not implement the C++ Standard correctly and with one
//                 of these the behavior is undefined is there is no memory).
//   s.pop()       If not empty, pops and returns the element that was
//                 popped, else throws a standard underflow_error.
//   s.peek()      If not empty, returns the top element, else throws a
//                 standard underflow_error.
// ---------------------------------------------------------------------------

#ifndef _SIMPLE_UNBOUNDED_STACK_H
#define _SIMPLE_UNBOUNDED_STACK_H

#include <stdexcept>
using namespace std;

// A stack object is represented as a singly-linked list of nodes.  The top
// of the stack is the first node.

template<class T>
class Stack {

// A private nested class of nodes, and a neat little routine
// that recursively blows away a whole linked chain of nodes.
private:
  class Node {
  public:
    T data;
    Node* next;
    Node(T data, Node* next): data(data), next(next) {}
  };
  static void clear(Node* n) {if (n != 0) {clear(n->next); delete n;}}
  
// Representation.  A single pointer field:  
private:
  Node* head;
  
// Constructors and Destructors
public:
  Stack(): head(0) {}
  ~Stack() {clear(head);}
  
// Behavior
public:
  int getSize() {
    int count = 0;
    for (Node* n = head; n != 0; n = n->next) count++;
    return count;
  }
  
  bool isEmpty() {
    return head == 0;
  }
  
  void push(T x) {
    head = new Node(x, head);
  }
  
  T pop() {
    if (head == 0) throw underflow_error("Cannot pop from empty stack");
    Node* nodeToDelete = head;
    T valueToReturn = head->data;
    head = head->next;
    delete nodeToDelete;
    return valueToReturn;
  }
  
  T peek() {
    if (head == 0) throw underflow_error("Cannot peek into empty stack");
    return head->data;
  }
  
// Prohibit copying and assignment
private:
  Stack(Stack&);
  Stack& operator=(Stack&);
};

#endif
// ---------------------------------------------------------------------------
// testunboundedstack.cpp                                                                 
//                                                                           
// A test script for the SimpleUnboundedStack class.
// ---------------------------------------------------------------------------

#include <iostream>
#include <cassert>
#include <cmath>
#include <stdexcept>
using namespace std;

#include "SimpleUnboundedStack.h"

int main(int argc, char** argv) {

  // Test an integer stack of testSize 8 for empty, full, and that
  // enqueing then dequeueing produces predicatble (LIFO) results.
  
  const int testSize = 100;
  Stack<int> s;

  assert(s.isEmpty() && s.getSize() == 0);
  s.push(0);
  assert(!s.isEmpty() && s.getSize() == 1);
  for (int i = 1; i < testSize; i++) {
    s.push(i);
  }
  assert(!s.isEmpty() && s.getSize() == testSize);
  for (int i = testSize - 1; i >= 0; i--) {
    assert(s.peek() == i);
    assert(s.pop() == i);
  }

  // Test underflow_errors are properly thrown on pops and peeks
  // when empty.  Also test that if an underflow is thrown, the stack
  // is not disturbed, i.e., it remains empty.
  
  assert(s.isEmpty());
  bool underflowHappened = false;
  try {
    s.pop();
  } 
  catch (underflow_error e) {
    underflowHappened = true;
  }
  assert(underflowHappened);
  assert(s.isEmpty());
  underflowHappened = false;
  try {
    s.peek();
  } 
  catch (underflow_error e) {
    underflowHappened = true;
  }
  assert(underflowHappened);
  
  cout << "All tests have passed\n";
  return 0;
}