// ---------------------------------------------------------------------------
// SimpleBoundedStack.h
//
// A generic bounded 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(n)      Constructor: declares a stack with room for n elements;
//                 default size is 256 elements.  Throws a bad_alloc if there
//                 is not enough memory to construct the stack.  Note: many
//                 compilers do not correctly implement Standard C++ and if
//                 you build this class with a non-standard compiler then the
//                 behavior is undefined if there is not enough memory for
//                 the stack.
//   ~Stack()      Destructor.
//   s.size()      Returns the number of elements in the stack.
//   s.isEmpty()   Returns whether the stack is empty.
//   s.isFull()    Returns whether the stack is full.
//   s.push(x)     If not full, pushes x, else throws a standard
//                 overflow_error.
//   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 overflow_error.
// ---------------------------------------------------------------------------

#ifndef _SIMPLE_BOUNDED_STACK_H
#define _SIMPLE_BOUNDED_STACK_H

#include <stdexcept>
using namespace std;

// A stack object wraps a low-level array indexed from 0 to capacity-1 where
// the bottommost element (if it exists) will be in slot 0.  The member top is
// the index of the slot above the top element, i.e. the next available slot
// that an element can go into.  Therefore if top==0 the stack is empty and
// if top==capacity it is full.

template<class T>
class Stack {
  T* data;
  int capacity;
  int top;
  
// Constructors and Destructors
public:
  explicit Stack(int n = 256): top(0), data(new T[n]), capacity(n) {}
  ~Stack() {delete[] data;}

// Behavior
public:
  int getSize() {return top;}
  bool isEmpty() {return top == 0;}
  bool isFull() {return top == capacity;}
  
  void push(T x) {
    if (isFull()) throw overflow_error("cannot push on full stack");
    data[top++] = x;
  }
  
  T pop() {
    if (isEmpty()) throw underflow_error("cannot pop from empty stack");
    return data[--top];
  }
  
  T peek() {
    if (isEmpty()) throw underflow_error("cannot peek into empty stack");
    return data[top - 1];
  }
  
// Prohibit copying and assignment
private:
  Stack(Stack&);
  Stack& operator=(Stack&);
};

#endif
// ---------------------------------------------------------------------------
// testboundedstack.cpp                                                                 
//                                                                           
// A test script for the SimpleBoundedStack class.
// ---------------------------------------------------------------------------

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

#include "SimpleBoundedStack.h"

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

  // Test that the default stack has capacity 256, is created empty,
  // fills after 256, and overflow_errors are thrown correctly.
  
  Stack<char> s1;
  assert(s1.isEmpty() && !s1.isFull() && s1.getSize() == 0);
  for (int i = 0; i < 256; i++) {
    s1.push('*');
  }
  assert(!s1.isEmpty() && s1.isFull());
  
  bool overflowHappened = false;
  try {
    s1.push('@');
  } 
  catch (overflow_error e) {
    overflowHappened = true;
  }
  assert(overflowHappened);

  // Test an integer stack of capacity 8 for empty, full, and that
  // pushing then popping produces predicatble (LIFO) results.
  
  const int capacity = 8;
  Stack<int> s2(capacity);

  assert(s2.isEmpty() && !s2.isFull());
  s2.push(0);
  assert(!s2.isEmpty() && !s2.isFull() && s2.getSize() == 1);
  for (int i = 1; i < capacity; i++) {
    s2.push(i);
  }
  assert(!s2.isEmpty() && s2.isFull() && s2.getSize() == capacity);
  for (int i = capacity - 1; i >= 0; i--) {
    assert(s2.peek() == i);
    assert(s2.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(s2.isEmpty());
  bool underflowHappened = false;
  try {
    s2.pop();
  } 
  catch (underflow_error e) {
    underflowHappened = true;
  }
  assert(underflowHappened);
  assert(s2.isEmpty());
  underflowHappened = false;
  try {
    s2.peek();
  } 
  catch (underflow_error e) {
    underflowHappened = true;
  }
  assert(underflowHappened);
  
  cout << "All tests have passed\n";
  return 0;
}