/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 
 * $Id: IntegerStack.java,v 1.1 2000/02/11 22:08:18 metlov Exp $
 *
 * This file is part of the Java Expressions Library (JEL).
 *   For more information about JEL visit :
 *    http://galaxy.fzu.cz/JEL/
 *
 * (c) 1998 -- 2000 by Konstantin Metlov(metlov@fzu.cz);
 *
 * JEL is Distributed under the terms of GNU General Public License.
 *    This code comes with ABSOLUTELY NO WARRANTY.
 *  For license details see COPYING file in this directory.
 */

package gnu.jel;

import gnu.jel.debug.Debug;
import gnu.jel.debug.Tester;

/**
 * Specialized stack which works with integers.
 */
class IntegerStack implements Cloneable {
  private int[] data;
  private int count=0;
  
  public IntegerStack(int initCapacity) {
    data=new int[initCapacity];
  };

  public IntegerStack() {
    this(30);
  };
  
  public Object clone() {
    IntegerStack res=null;
    try {
      res=(IntegerStack)super.clone();
      res.data=(int[])res.data.clone();
    } catch (CloneNotSupportedException exc) {
      if (Debug.enabled)
        Debug.reportThrowable(exc);
    };
    return res;
  };

  public final void push(int what) {
    if (count>=data.length) increaseCapacity(count+1);
    data[count++]=what;
  };
  
  public final int peek() {
    return data[count-1];
  };

  public final int peek(int i) {
    return data[count-1-i];
  };
  
  public final void push(int what,int i) {
    if (count>=data.length) increaseCapacity(count+1);
    i=count-i;
    System.arraycopy(data,i,data,i+1,count-i);
    data[i]=what;
    count++;
  };

  public final int pop() {
    return data[--count];
  };

  public final int size() {
    return count;
  };

  public final void pop_throw() {
    count--;
    if (Debug.enabled)
      Debug.assert(count>=0,"Stack is empty when removing top.");
  };

  // Swaps values above given limits in two stacks
  public static void swap(IntegerStack one,int oneLim,
                          IntegerStack other,int otherLim) {
    int copyFromOne=one.count-oneLim;
    int copyFromOther=other.count-otherLim;
    boolean cf_one=copyFromOne>0;
    boolean cf_other=copyFromOther>0;
    if ((cf_one) || (cf_other)) {
      int nSizeOne=oneLim+copyFromOther;
      int nSizeOther=otherLim+copyFromOne;
      
      // ensure capacities
      if (nSizeOne>one.data.length) one.increaseCapacity(nSizeOne);
      if (nSizeOther>other.data.length) other.increaseCapacity(nSizeOther);
      
      int[] temp=null;
      if (cf_one) {
        temp=new int[copyFromOne];
        System.arraycopy(one.data,oneLim,temp,0,copyFromOne);
      };
      if (cf_other)
        System.arraycopy(other.data,otherLim,one.data,oneLim,copyFromOther);

      if (cf_one)
        System.arraycopy(temp,0,other.data,otherLim,copyFromOne);
      
      one.count=nSizeOne;
      other.count=nSizeOther;
    };
  };

  private synchronized void increaseCapacity(int minCapacity) {
    int[] old_data=data;
    int oldSize=data.length;
    int newSize=oldSize*2;
    if (newSize<minCapacity) newSize=minCapacity;
    data=new int[newSize];
    System.arraycopy(old_data,0,data,0,count);
  };

  //================================================================
  //======================== UNITARY TESTS =========================
  //================================================================

  /**
   * Performs unitary test of this class.
   * @param args ignored.
   */
  public static void main(String[] args) {
    if (Debug.enabled) {
      Tester t=new Tester(System.out);
      test(t);
      t.summarize();
    };
  };

  /**
   * Performs unitary test of this class.
   * <p> Used if all package is being tested and not just codegen.
   * @param t Tester to report test results.
   */
  public static void test(Tester t) {
    if (Debug.enabled) {
      IntegerStack is=null;
      try {
        t.startTest("Make a new stack.");
        is=new IntegerStack(1);
        t.testOK();
      } catch (Throwable e) {
        t.testFail();
        Debug.reportThrowable(e);
      };

      try {
        t.startTest("push(10)");
        is.push(10);
        t.testOK();
      } catch (Throwable e) {
        t.testFail();
        Debug.reportThrowable(e);
      };

      try {
        t.startTest("peek()==10");
        t.compare(is.peek(),10);
      } catch (Throwable e) {
        t.testFail();
        Debug.reportThrowable(e);
      };

      try {
        t.startTest("push(10); pop()==10");
        is.push(10);
        t.compare(is.pop(),10);
      } catch (Throwable e) {
        t.testFail();
        Debug.reportThrowable(e);
      };

      try {
        t.startTest("push(11)");
        is.push(11);
        t.testOK();
      } catch (Throwable e) {
        t.testFail();
        Debug.reportThrowable(e);
      };

      try {
        t.startTest("peek(0)=11");
        t.compare(is.peek(0),11);
      } catch (Throwable e) {
        t.testFail();
        Debug.reportThrowable(e);
      };

      try {
        t.startTest("peek(1)=10");
        t.compare(is.peek(1),10);
      } catch (Throwable e) {
        t.testFail();
        Debug.reportThrowable(e);
      };

      try {
        t.startTest("push(12,0); pop()==12");
        is.push(12,0);
        t.compare(is.pop(),12);
      } catch (Throwable e) {
        t.testFail();
        Debug.reportThrowable(e);
      };

      try {
        t.startTest("push(12,1); pop()==11");
        is.push(12,1);
        t.compare(is.pop(),11);
      } catch (Throwable e) {
        t.testFail();
        Debug.reportThrowable(e);
      };

      try {
        t.startTest("pop()==12");
        t.compare(is.pop(),12);
      } catch (Throwable e) {
        t.testFail();
        Debug.reportThrowable(e);
      };

      try {
        t.startTest("pop()==10");
        t.compare(is.pop(),10);
      } catch (Throwable e) {
        t.testFail();
        Debug.reportThrowable(e);
      };

      try {
        t.startTest("size()==0");
        t.compare(is.size(),0);
      } catch (Throwable e) {
        t.testFail();
        Debug.reportThrowable(e);
      };


    };
  };

};
