
// Signature
//   IntStack emptyStack ();
//   IntStack push (int);
//   boolean isEmpty ();
//   int top ();
//   IntStack pop ();
//   boolean equals (Object);
//
// Specs:    
//   s.push(i).top() == i
//   s.push(i).pop() == s
//   s.push(i).isEmpty() == false
//   IntStack.emptyStack().isEmpty() == true
//   IntStack.emptyStack().equals (obj) ==
//     obj.isEmpty()  if obj is a IntStack
//     false          otherwise
//   s.push(i).equals (obj) ==
//     false          if obj is a IntStack & obj.isEmpty()==true
//     obj.top()==i && s.equals(obj.pop())  
//                    if obj is a IntStack & obj.isEmpty()==false
//     false          otherwise

import java.util.Random;

public class IntStackTester {


    /** Main testing method
     */
    public static void main(String[] args){
        /* Create a new Tester, run the tests, and Print out results. */
        IntStackTester t = new IntStackTester ();
       
        /* See definitions below */
        t.testIntStacks();
        t.testExceptions();

        /* A few Stats... */
        System.out.println("\nNumber of stacks tested: " + t.totStacks);
        System.out.println("Number of tests performed: " + t.ntests);
        System.out.println("Number of errors found: " + t.nerr);
    }

    private int ntests = 0;     // Total Tests Run
    private int nerr = 0;          // Number of Errors
    private int totStacks = 0; // Number of Stacks Created
    
    /* How many stacks to create? */
    private static final int NUM_TO_TEST = 200;

    /* Create random stacks, and test them */
    private void testIntStacks(){
        Random r = new Random();
	// first off, make sure we test the empty stack
	testIntStack (new int[0],r.nextInt(10));
	// bunch of random stacks (random size)
        for(int i = 0; i < NUM_TO_TEST; i++){
	    int size = r.nextInt(5); // number of elements in stack
	    int[] vals = new int[size];
	    for (int j=0; j < size; j++) 
		vals[j]=r.nextInt(10);
            /* Run the Tests */
	    testIntStack (vals,r.nextInt(10));
        }
    }

    /* Test a single Stack */
    private void testIntStack (int[] vals,int next){
        try{
            /* Create the stack from the array of ints */
            IntStack s = IntStack.emptyStack ();
	    for (int i=0; i<vals.length; i++)
		s = s.push (vals[i]);

            /* Update the numbers for our static method checks later*/
            totStacks++;

	    /* check accessors */
	    assertTrue(s.push(next).top() == next, "s.push(i).top()==i");
	    assertTrue(s.push(next).pop() == s, "s.push(i).pop()==s");
	    assertTrue(s.push(next).isEmpty() == false, "s.push(i).isEmpty()==false");

	    /* for the empty stack, make sure isEmpty is correct */
	    if (vals.length == 0)
		assertTrue (s.isEmpty() == true, "IntStack.emptyStack.isEmpty()==true");



	    /*
	      Here, you want to test the .equals() method.
	      some hints: 
	      - you definitely want to test that .equals(null) is false. 
	      - you may want to construct an equal object and an 
	        unequal object to make sure that .equals() returns the
		right result for those
	      - you may want to check reflexivity, symmetry, transitivity. 
	        (the first two are easy, the third is a bit of a pain).
	    */


        } catch(RuntimeException e) {
            
            /* If there was an exception anywhere in there, then we
             *   have a problem */
            assertTrue(false, "Exception: "+e.getMessage());
        }
    }



    /* Make sure exceptions are thrown for border cases  */
    
    /* We make sure that good tests get counted by using:
     *       assertTrue(true,""); 
     *   and force bad tests to fail with:
     *       assertTrue(false,"Failure Message"); 
     */
    private void testExceptions() {
        /* top/pop of empty stack */
        try { 
	    int result = IntStack.emptyStack().top();
            assertTrue (false, "IntStack.emptyStack().top():  No Exception");
        } catch(RuntimeException e) { 
	    assertTrue (true, ""); 
	}
        
	try { 
	    IntStack s = IntStack.emptyStack().pop();
	    assertTrue (false, "IntStack.emptyStack().pop(): No Exception");
        } catch(RuntimeException e) { 
	    assertTrue (true, ""); 
	}

    }

    /* Number of dots to print before we go to the next line */
    private static final int DOTS_PER_LINE = 50;

    /* Update the test counters based on the result given. Result
     *   is expected to be true for passing tests, and false for
     *   failing tests.  If a test fails, we print out the provided
     *   message so the user can see what might have gone wrong. 
     * Be sure to review anything that doesn't make sense. */
    private void assertTrue (boolean result, String msg){
        ntests++;
        if(!result){
            System.out.println("\n**ERROR**: test# " + ntests + " -- " + msg);
            nerr ++;
        }
        if(ntests % DOTS_PER_LINE == 0)System.out.println();
        System.out.print(".");
    }
}

