GCalc Source Code

/*	GCalc Graphing Calculator Applet
	Version 1.5

	Copyright 2001 Jiho Kim
*/
//package GCalc;


import java.applet.Applet;
import java.awt.event.*;
import java.awt.*;
import java.util.*;

public class GCalc extends Applet implements ActionListener, ItemListener
{
    String notice = "GCalc\nVersion 1.501\nCopyright 1999-2001 Jiho Kim\n";

	TextField textfield1;
	Graph graph1;

	Button axisButton, scoresButton, gridButton, dotsButton;

	Button standardZoomButton, trigZoomButton, squareZoomButton, graphFitZoomButton;
	Button zoomInButton, zoomOutButton;
	TextField xZoom, yZoom;

	TextField xminTF, xmaxTF, xsclTF,
			  yminTF, ymaxTF, ysclTF;

	Button resetButton, previousButton, nextButton;

	Button regraphButton;

	Choice colorChoice;
	String[] colorString;
	Color[] colorArray;

	StringList sl;

	public static void main(String[] args)
	{
		Frame f = new Frame("GCalc");

		GCalc gc = new GCalc();

		gc.init();
		gc.start();

		f.addWindowListener(f);
		f.add("Center",gc);
		f.setSize(700,430);
		f.setResizable(false);
		f.show();

	}

	public void init()
	{
		initApplet();
		initGUI();

	}

	void initApplet()
	{
		setVisible(true);
		setBackground(new Color(240,240,200));
		setLayout(new BorderLayout(5,5));

		graph1 = new Graph(this,460,330);

		colorString = new String[5];
		colorArray = new Color[5];
		colorString[0]="Blue";			colorArray[0]=Color.blue;
		colorString[1]="Green";			colorArray[1]=Color.green.darker();
		colorString[2]="Purple";		colorArray[2]=Color.magenta.darker();
		colorString[3]="Brown";			colorArray[3]=Color.orange.darker();
		colorString[4]="Black";			colorArray[4]=Color.black;

		sl = new StringList();
	}

	void initGUI()
	{
		Font normalFont = new Font("Courier", Font.PLAIN, 12);

		Panel graphProperties = new Panel();
		graphProperties.setLayout(new GridLayout(1,4,1,0));

		axisButton = new Button("Axis ON");
		scoresButton = new Button ("Scale ON");
		gridButton = new Button("Grid OFF");
		dotsButton = new Button("Continuous");

		axisButton.addActionListener(this);
		scoresButton.addActionListener(this);
		gridButton.addActionListener(this);
		dotsButton.addActionListener(this);

		graphProperties.add(axisButton);
		graphProperties.add(scoresButton);
		graphProperties.add(gridButton);
		graphProperties.add(dotsButton);

	//Center--Graph, input textfield, graph properties
		Panel west=new Panel();

		textfield1 = new TextField("",42);
		textfield1.setFont(new Font("Courier", Font.PLAIN, 12));
		textfield1.setBackground(Color.white);
		textfield1.addActionListener(this);

		Panel westCenter = new Panel();
		westCenter.setLayout(new BorderLayout(5,5));

		westCenter.add("North",textfield1);
		westCenter.add("Center",graph1);
		westCenter.add("South",graphProperties);
		west.add(westCenter);
		add("West",west);

	//East--zoom Buttons7
		standardZoomButton = new Button("Standard Zoom");
		trigZoomButton = new Button ("Trig Zoom");
		squareZoomButton = new Button("Square Zoom");
		graphFitZoomButton = new Button("Graph-Fit Zoom");

		standardZoomButton.addActionListener(this);
		trigZoomButton.addActionListener(this);
		squareZoomButton.addActionListener(this);
		graphFitZoomButton.addActionListener(this);

		Panel zoomButtons = new Panel();
		zoomButtons.setLayout(new GridLayout(4,1));
		zoomButtons.add(standardZoomButton);
		zoomButtons.add(trigZoomButton);
		zoomButtons.add(squareZoomButton);
		zoomButtons.add(graphFitZoomButton);

		zoomInButton = new Button("Zoom In");
		zoomOutButton = new Button("Zoom Out");
		xZoom = new TextField("2.0");
		yZoom = new TextField("2.0");
		xZoom.setFont(normalFont);
		yZoom.setFont(normalFont);
		xZoom.setBackground(Color.white);
		yZoom.setBackground(Color.white);

		zoomInButton.addActionListener(this);
		zoomOutButton.addActionListener(this);
		xZoom.addActionListener(this);
		yZoom.addActionListener(this);

		Panel manualZooms = new Panel();
		manualZooms.setLayout(new GridLayout(4,1));
		manualZooms.add(zoomInButton);
		manualZooms.add(zoomOutButton);
		manualZooms.add(xZoom);
		manualZooms.add(yZoom);

	//East--range fields
		xminTF = new TextField("",20);
		xmaxTF = new TextField("",20);
		xsclTF = new TextField("",20);
		yminTF = new TextField("",20);
		ymaxTF = new TextField("",20);
		ysclTF = new TextField("",20);

		xminTF.addActionListener(this);
		xmaxTF.addActionListener(this);
		xsclTF.addActionListener(this);
		yminTF.addActionListener(this);
		ymaxTF.addActionListener(this);
		ysclTF.addActionListener(this);

		xminTF.setFont(normalFont);
		xmaxTF.setFont(normalFont);
		xsclTF.setFont(normalFont);
		yminTF.setFont(normalFont);
		ymaxTF.setFont(normalFont);
		ysclTF.setFont(normalFont);

		xminTF.setBackground(Color.white);
		xmaxTF.setBackground(Color.white);
		xsclTF.setBackground(Color.white);
		yminTF.setBackground(Color.white);
		ymaxTF.setBackground(Color.white);
		ysclTF.setBackground(Color.white);

		Panel xminPanel = new Panel();
		Panel xmaxPanel = new Panel();
		Panel xsclPanel = new Panel();
		Panel yminPanel = new Panel();
		Panel ymaxPanel = new Panel();
		Panel ysclPanel = new Panel();

		xminPanel.setLayout(new BorderLayout());
		xmaxPanel.setLayout(new BorderLayout());
		xsclPanel.setLayout(new BorderLayout());
		yminPanel.setLayout(new BorderLayout());
		ymaxPanel.setLayout(new BorderLayout());
		ysclPanel.setLayout(new BorderLayout());

		xminPanel.add("Center",new Label ("XMin", Label.LEFT));
		xminPanel.add("East",xminTF);
		xmaxPanel.add("Center",new Label ("XMax", Label.LEFT));
		xmaxPanel.add("East",xmaxTF);
		xsclPanel.add("Center",new Label ("XScale", Label.LEFT));
		xsclPanel.add("East",xsclTF);
		yminPanel.add("Center",new Label ("YMin", Label.LEFT));
		yminPanel.add("East",yminTF);
		ymaxPanel.add("Center",new Label ("YMax", Label.LEFT));
		ymaxPanel.add("East",ymaxTF);
		ysclPanel.add("Center",new Label ("YScale", Label.LEFT));
		ysclPanel.add("East",ysclTF);

		Panel xyRange = new Panel();

		xyRange.setLayout(new GridLayout(6,1));
		xyRange.add(xminPanel);
		xyRange.add(xmaxPanel);
		xyRange.add(xsclPanel);
		xyRange.add(yminPanel);
		xyRange.add(ymaxPanel);
		xyRange.add(ysclPanel);

	//East--misc buttons
		resetButton = new Button ("RESET");
		previousButton = new Button ("Prev");
		nextButton = new Button("Next");

		previousButton.addActionListener(this);
		nextButton.addActionListener(this);
		resetButton.addActionListener(this);

		colorChoice = new Choice();
		colorChoice.addItemListener(this);
		for (int i=0; imax) max=yy;
						}
					}
				}

				if (max!=Double.MIN_VALUE && min!=Double.MAX_VALUE && max!=min &&
					min!=Double.NEGATIVE_INFINITY && max!=Double.POSITIVE_INFINITY)
				{
					graph1.changeRange(graph1.xmin,graph1.xmax,graph1.xscl,
											min,max,graph1.yscl);
					graph1.clearGraph();
					graph1.drawPostfix();
				}
			}
			else
			{
				graphFitZoomButton.setLabel("In progress");
				graph1.fitToZoomBox();
				graph1.drawPostfix();
			}
			updateRangeTF();
			graphFitZoomButton.setLabel("Graph-Fit Zoom");
		}
		else
		if (target == standardZoomButton)
		{
			standardZoomButton.setLabel("In progress");
			graph1.changeRange(-10,10,1,-10,10,1);
			graph1.clearGraph();
			graph1.drawPostfix();
			updateRangeTF();
			standardZoomButton.setLabel("Standard Zoom");
		}
		else
		if (target == trigZoomButton)
		{
			trigZoomButton.setLabel("In progress");
			graph1.changeRange(-2.5*Math.PI,2.5*Math.PI,Math.PI/2,-4,4,1);
			graph1.clearGraph();
			graph1.drawPostfix();
			updateRangeTF();
			trigZoomButton.setLabel("Trig Zoom");
		}
		else
		if (target == squareZoomButton)
		{
			squareZoomButton.setLabel("In progress");
			double d = graph1.ypixels*(graph1.xmax-graph1.xmin)/2/graph1.xpixels;
			graph1.changeRange(graph1.xmin,graph1.xmax,graph1.xscl,
								graph1.yavg-d,graph1.yavg+d,graph1.yscl);
			graph1.clearGraph();
			graph1.drawPostfix();
			updateRangeTF();
			squareZoomButton.setLabel("Square Zoom");
		}
		else
		if (target == zoomInButton)
		{
			zoomInButton.setLabel("In progress");
			setZoomValues();
			double d = (graph1.xmax-graph1.xmin)/graph1.xzoom/2;
			double e = (graph1.ymax-graph1.ymin)/graph1.yzoom/2;
			graph1.changeRange(graph1.xavg-d,graph1.xavg+d,graph1.xscl,
								graph1.yavg-e,graph1.yavg+e,graph1.yscl);
			graph1.clearGraph();
			graph1.drawPostfix();
			updateRangeTF();
			updateZoomTF();
			zoomInButton.setLabel("Zoom In");
		}
		else
		if (target == zoomOutButton)
		{
			zoomOutButton.setLabel("In progress");
			setZoomValues();
			double d = (graph1.xmax-graph1.xmin)*graph1.xzoom/2;
			double e = (graph1.ymax-graph1.ymin)*graph1.yzoom/2;
			graph1.changeRange(graph1.xavg-d,graph1.xavg+d,graph1.xscl,
								graph1.yavg-e,graph1.yavg+e,graph1.yscl);
			graph1.clearGraph();
			graph1.drawPostfix();
			updateRangeTF();
			updateZoomTF();
			zoomOutButton.setLabel("Zoom Out");
		}
		else
		if (target == xmaxTF || target == xminTF || target == ymaxTF ||
			target == yminTF ||	target == xsclTF || target == ysclTF )
		{
			setRangeValues();
			graph1.clearGraph();
			graph1.drawPostfix();
		}
		else
		if (target==resetButton)
		{
			setRangeValues();
			graph1.clearList();
			graph1.clearGraph();
			textfield1.setText("");
			colorChoice.select(0);
			sl.clear();
		}
		else
		if (target == previousButton)
		{
			textfield1.setText(sl.getPrev());
		}
		else
		if (target == nextButton)
		{
			textfield1.setText(sl.getNext());
		}
	}

	void processUserInput(String strInput)
	{
		String mystr = strInput.trim().toLowerCase();
		String postfixText="";

		int commandType = 0;

		if (mystr.equals("clear"))
		{
			commandType = 3;
			graph1.clearList();
			graph1.clearGraph();
			textfield1.setText("");
			colorChoice.select(0);
			sl.clear();

			System.gc();
		}
/*		else if (mystr.equals("hide"))
		{
			commandType = 1;
			graph1.setVisible(! graph1.isVisible());
			graph1.clearGraph();
			graph1.drawPostfix();
			textfield1.setText("");
		}
*/		else if (mystr.equals("axis"))
		{
			commandType = 1;
			graph1.axis = ! graph1.axis;
			graph1.clearGraph();
			textfield1.setText("");
		}
		else if (mystr.equals("tick"))
		{
			commandType = 1;
			graph1.scores = ! graph1.scores;
			graph1.clearGraph();
			textfield1.setText("");
		}
		else if (mystr.equals("dots"))
		{
			commandType = 1;
			graph1.dots = ! graph1.dots;
			graph1.clearGraph();
			graph1.drawPostfix();
			textfield1.setText("");
		}
		else if (mystr.equals("grid"))
		{
			commandType = 1;
			graph1.grid = ! graph1.grid;
			graph1.clearGraph();
			textfield1.setText("");
		}
		else if (mystr.equals("prev"))
		{
			commandType = 1;
			textfield1.setText(sl.getPrev());
		}
		else if (mystr.equals("next"))
		{
			commandType = 1;
			textfield1.setText(sl.getNext());
		}
		else if (mystr.startsWith("~"))
		{
			commandType = 1;
			int val=0;
			try
			{
				Integer d = Integer.valueOf(mystr.substring(1));
				val = d.intValue();
			}
			catch (NumberFormatException exception) {}

			graph1.remove(val);
			graph1.clearGraph();
			graph1.drawPostfix();
			textfield1.setText("");
		}
		else if (mystr.startsWith("list"))
		{
			commandType = 1;

			graph1.getList().showInfix();
			textfield1.setText("");
		}
		else if (mystr.startsWith("#"))
		{
			commandType = 1;
			int val=0;
			try
			{
				Integer d = Integer.valueOf(mystr.substring(1));
				val = d.intValue();
			}
			catch (NumberFormatException exception) {}

			textfield1.setText(graph1.getList().get(val).infixStr);
		}

		Postfix postfix=null;

		if (commandType==0)
		{
			long timer = System.currentTimeMillis();
			commandType = 2;

			sl.add(mystr);
			sl.resetCursor();

			setRangeValues();

			postfix = new Postfix(mystr);
			postfixText = postfix.getStack().showAll();

			graph1.drawPostfix(postfix, colorArray[colorChoice.getSelectedIndex()]);
//			graph1.drawPostfix(postfix.derivative(), new Color(200,200,200));

			graph1.drawPostfix();

			textfield1.setText("");
			colorChoice.select((colorChoice.getSelectedIndex()+1)%colorArray.length);
		}

		if (commandType==2 && !postfixText.equals(""))
		{
			System.out.println("INFIX: "+mystr);
			System.out.println("POSTFIX: "+postfixText);
//			System.out.println("FULLY PARENTHESIZED INFIX: "+postfix.infix());
//			System.out.println("DERIVATIVE:"+postfix.derivative().infix());
			System.out.println();
		}
	}

	public void setZoomValues()
	{
		double xz, yz;

		try
		{
			Double d = Double.valueOf(xZoom.getText());
			xz = d.doubleValue();
		}
		catch (NumberFormatException exception)
		{
			xz = graph1.xzoom;
		}

		try
		{
			Double d = Double.valueOf(yZoom.getText());
			yz = d.doubleValue();
		}
		catch (NumberFormatException exception)
		{
			yz = graph1.yzoom;
		}

		if (xz*yz!=0)
		{
			graph1.yzoom = Math.abs(yz);
			graph1.xzoom = Math.abs(xz);
			updateZoomTF();
		}
	}

	public void setRangeValues()
	{
		double xmax1, xmin1, xscl1;
		double ymax1, ymin1, yscl1;

		Postfix pf;

		pf = new Postfix(xminTF.getText());
		xmin1=pf.isConstant()?pf.evaluate(0):graph1.xmin;

		pf = new Postfix(xmaxTF.getText());
		xmax1=pf.isConstant()?pf.evaluate(0):graph1.xmax;

		pf = new Postfix(xsclTF.getText());
		xscl1=pf.isConstant()?pf.evaluate(0):graph1.xscl;

		pf = new Postfix(yminTF.getText());
		ymin1=pf.isConstant()?pf.evaluate(0):graph1.ymin;

		pf = new Postfix(ymaxTF.getText());
		ymax1=pf.isConstant()?pf.evaluate(0):graph1.ymax;

		pf = new Postfix(ysclTF.getText());
		yscl1=pf.isConstant()?pf.evaluate(0):graph1.yscl;

		if (xmax1>xmin1 && ymax1>ymin1 /*&& yscl1!=0 && xscl1!=0*/)
		{
			graph1.changeRange(xmin1, xmax1, xscl1, ymin1, ymax1, yscl1);
			updateRangeTF();
		}
	}

	void updateRangeTF()
	{
		xmaxTF.setText(""+graph1.xmax);
		xminTF.setText(""+graph1.xmin);
		xsclTF.setText(""+graph1.xscl);
		ymaxTF.setText(""+graph1.ymax);
		yminTF.setText(""+graph1.ymin);
		ysclTF.setText(""+graph1.yscl);
	}

	void updateZoomTF()
	{
		xZoom.setText(""+graph1.xzoom);
		yZoom.setText(""+graph1.yzoom);
	}

}

class Graph extends Canvas implements MouseListener, MouseMotionListener
{
	private GCalc host;
	private Image image;
	private Graphics page;

	public boolean axis, dots, scores, grid;

	public double xmax, xmin, xscl, xscl2, xavg, xfact;
	public double ymax, ymin, yscl, yscl2, yavg, yfact;
	public double xzoom, yzoom;

	public int xpixels, ypixels;

	private PostfixList pfl;

	private float mx, my;
	public int dragstartX, dragstartY;
	public int dragdX, dragdY;

	int preferredWidth;
	int preferredHeight;

	Graph (GCalc gc, int xSize, int ySize)
	{
		this(gc, xSize, ySize, -10, 10, 1, -10, 10, 1);
	}

	Graph (GCalc gc, int xSize, int ySize, double xmin1, double xmax1, double xscl1,
								double ymin1, double ymax1, double yscl1)
	{
		host = gc;
		xpixels=xSize;
		ypixels=ySize;

		setSize(xpixels+1,ypixels+1);

 		changeRange(xmin1, xmax1, xscl1, ymin1, ymax1, yscl1);

		initDefault();
		clearList();

		addMouseListener(this);
		addMouseMotionListener(this);

	}


	private void initDefault()
	{
		axis = true;
		dots = false;
		scores = true;
		grid = false;

		xzoom=2;
		yzoom=2;

		my=0;
		mx=0;

		dragstartX=-1;
		dragstartY=-1;
		dragdX=0;
		dragdY=0;
	}

	public void mouseClicked (MouseEvent event)
	{
		switch (event.getClickCount())
		{
			case 1:
				break;
			default:
				double x=event.getX()/xfact+xmin;
				double y=event.getY()/yfact+ymax;
				changeRange(x-(xmax-xmin)/2, x+(xmax-xmin)/2, xscl,
								y-(ymax-ymin)/2, y+(ymax-ymin)/2, yscl);
								
				host.updateRangeTF();
				clearGraph();
				drawPostfix();
				break;
		}
		mouseMoved(event);
	}

	public void mouseMoved (MouseEvent event)
	{
		if (page!=null)
		{
			page.setPaintMode();
			referencePt((float) (event.getX()/xfact+xmin),(float) (event.getY()/yfact+ymax));
			drawCoordinate();
		}
		repaint();
	}

	public void referencePt(float xx, float yy)
	{
		mx=xx;
		my=yy;
	}

	public void mouseDragged (MouseEvent event)
	{
		if (dragstartX==-1 && dragstartY==-1)
		{
			dragstartX=event.getX();
			dragstartY=event.getY();
			dragdX=0;
			dragdY=0;
		}
		else
		{
			page.setXORMode(Color.white);
			page.setColor(Color.orange);
			page.drawRect(dragstartX-dragdX, dragstartY-dragdY, 2*dragdX, 2*dragdY);
			dragdX=Math.abs(event.getX()-dragstartX);
			dragdY=Math.abs(event.getY()-dragstartY);
			page.drawRect(dragstartX-dragdX, dragstartY-dragdY, 2*dragdX, 2*dragdY);
			mouseMoved(event);
		}
	}

	public void mouseReleased (MouseEvent event)
	{
		page.setPaintMode();

		if (dragstartX!=-1 && dragstartY!=-1)
			host.graphFitZoomButton.setLabel("Box Zoom");
		else
			host.graphFitZoomButton.setLabel("Graph-Fit Zoom");
	}

	public void mousePressed (MouseEvent event)
	{
		if (dragstartX!=-1 && dragstartY!=-1)
		{
			page.setXORMode(Color.white);
			page.setColor(Color.orange);
			page.drawRect(dragstartX-dragdX, dragstartY-dragdY, 2*dragdX, 2*dragdY);
//			dragstartX=-1;
//			dragstartY=-1;
			dragstartX=event.getX();
			dragstartY=event.getY();
			dragdX=0;
			dragdY=0;
		}
	}

	public void mouseEntered (MouseEvent event)
	{
		mouseMoved(event);
	}

	public void mouseExited (MouseEvent event)
	{
		mx=Float.NaN;
		my=Float.NaN;
		drawCoordinate();
	}

	private void drawCoordinate()
	{
		page.setColor(Color.gray);
		page.fillRect(3,2,120,23);
		page.setColor(Color.lightGray);
		page.fillRect(0,0,120,23);
		page.setColor(Color.black);
		page.drawString("x = "+nonNaNString(mx), 5, 10);
		page.drawString("y = "+nonNaNString(my), 5, 20);

		repaint();
	}
	
	private static String nonNaNString(float a)
	{
		if (Float.isNaN(a)) return "";
		
		return ""+a;
	}

	public void changeRange (double xmin1, double xmax1, double xscl1,
								double ymin1, double ymax1, double yscl1)
	{
		if (xmax1>xmin1 && ymax1>ymin1 &&
			!isNotReal(xmin1) && !isNotReal(xmax1) && !isNotReal(xscl1) &&
			!isNotReal(ymin1) && !isNotReal(ymax1) && !isNotReal(yscl1)	)
		{
			xscl = Math.abs(xscl1);
			yscl = Math.abs(yscl1);
			xmax = xmax1;
			xmin = xmin1;
			ymax = ymax1;
			ymin = ymin1;
		}

		xavg = (xmax+xmin)/2;	xfact=xpixels/(xmax-xmin);
		yavg = (ymax+ymin)/2;	yfact=ypixels/(ymin-ymax);

		if (image!=null) clearGraph();
	}

	public void fitToZoomBox()
	{
		double xmin2=(dragstartX-dragdX)/xfact+xmin;
		double xmax2=(dragstartX+dragdX)/xfact+xmin;
		double ymax2=(dragstartY-dragdY)/yfact+ymax;
		double ymin2=(dragstartY+dragdY)/yfact+ymax;

		changeRange(xmin2,xmax2,xscl,
					ymin2,ymax2,yscl);
	}

	public void paint(Graphics g)
	{
		update(g);
	}

	public void update(Graphics g)
	{
		if (image==null)
		{
			image = createImage(xpixels+1,ypixels+1);
			page = image.getGraphics();
			clearGraph();
		}

		g.drawImage(image, 0,0, this);
	}

	public void clearList()
	{
		pfl=new PostfixList();
	}

	public void clearGraph()
	{
		if (image==null) return;

		page.setColor(Color.white);
		page.fillRect(0,0,xpixels+1,ypixels+1);

		if (grid) drawGrid();
		if (axis) drawAxis();
		if (scores) drawScores();

		pfl.deflagAll();
		referencePt(Float.NaN,Float.NaN);
		drawCoordinate();

		dragstartX=-1;
		dragstartY=-1;
		dragdX=0;
		dragdY=0;

		host.graphFitZoomButton.setLabel("Graph-Fit Zoom");

	}

	private void drawAxis()
	{
		int sx = (int) (-xmin*xfact+.5);	//(sx,sy) is where the origin is
		int sy = (int) (-ymax*yfact+.5);

		page.setColor(Color.red);
		if (YonScreen(sy))
			page.drawLine(0,sy,xpixels-1,sy);

		if (XonScreen(sx))
			page.drawLine(sx,0,sx,ypixels-1);
	}

	private void saveScl()
	{
		xscl2 = xscl;
		yscl2 = yscl;
		if (xscl!=0 && 1/xscl>Math.abs(3*xfact))
			xscl = Math.abs(1/xfact);
		if (yscl!=0 && 1/yscl>Math.abs(3*yfact))
			yscl = Math.abs(1/yfact);
	}

	private void restoreScl()
	{
		xscl = xscl2;
		yscl = yscl2;
	}

	private void drawGrid()
	{
		int sx = (int) (-xmin*xfact+.5);	//(sx,sy) is where the origin is
		int sy = (int) (-ymax*yfact+.5);

		saveScl();

		page.setColor(Color.lightGray);
		if (xscl!=0)
		{
			for (double i = xscl*((int) (xmin/xscl+.5)); i<=xmax; i+=xscl)
			{
				int sx1 = (int) ((i-xmin)*xfact+.5);
				int sx2 = (int) ((-i-xmin)*xfact+.5);
				page.drawLine(sx1,0,sx1,ypixels-1);
				page.drawLine(sx2,0,sx2,ypixels-1);
			}
		}

		if (yscl!=0)
		{
			for (double i = yscl*((int) (ymin/yscl+.5)); i<=ymax; i+=yscl)
			{
				int sy1 = (int) ((i-ymax)*yfact+.5);
				int sy2 = (int) ((-i-ymax)*yfact+.5);
				page.drawLine(0,sy1,xpixels-1,sy1);
				page.drawLine(0,sy2,xpixels-1,sy2);
			}
		}

		restoreScl();

	}

	private void drawScores()
	{
		int sx = (int) (-xmin*xfact+.5);	//(sx,sy) is where the origin is
		int sy = (int) (-ymax*yfact+.5);

		saveScl();

		page.setColor(Color.blue);
		if (YonScreen(sy) && xscl!=0)						//Draw X axis
		{
			for (double i = xscl*((int) (xmin/xscl+.5)); i<=xmax; i+=xscl)
			{
				int sx1 = (int) ((i-xmin)*xfact+.5);
				int sx2 = (int) ((-i-xmin)*xfact+.5);
				page.drawLine(sx1,sy-1,sx1,sy+1);
				page.drawLine(sx2,sy-1,sx2,sy+1);
			}
		}
		if (XonScreen(sx) && yscl!=0)						//Draw Y axis
		{
			for (double i = yscl*((int) (ymin/yscl+.5)); i<=ymax; i+=yscl)
			{
				int sy1 = (int) ((i-ymax)*yfact+.5);
				int sy2 = (int) ((-i-ymax)*yfact+.5);
				page.drawLine(sx-1,sy1,sx+1,sy1);
				page.drawLine(sx-1,sy2,sx+1,sy2);
			}
		}

		restoreScl();
	}

	private boolean YoutOfBounds (double y)
	{
		if (Double.isInfinite(y)) return true;
		if (Double.isNaN(y)) return true;
		if (yymax) return true;

		return false;
	}

	private boolean XoutOfBounds (double x)
	{
		if (Double.isInfinite(x)) return true;
		if (Double.isNaN(x)) return true;
		if (xxmax) return true;

		return false;
	}

	private boolean YonScreen (double y)
	{
		return (y>=0 && y=0 && x0)
						ws.push(1);
					else
					if (oper1<0)
						ws.push(-1);
					else
						ws.push(0);
				}
				}
			}
		}
		catch (NullPointerException exception)
		{
			//Primative malformed input detection.
			ws = new StackDoub();
			ws.push(Double.MIN_VALUE);
		}

		if (! ws.isEmpty())
			a=ws.pop();
		else
			a=Double.NaN;
		return a;
	}

	public Stack getStack()
	{
		return pf.copy();
	}

	public boolean isConstant()
	{
		return isConstant;
	}

	public String infix()
	{
		if (pf!=null)
			return infix(pf.copy());

		return "?:?";
	}

	private static String infix(Stack s)
	{
		String str = "";

		Token tk = s.pop();

		if (tk.isBinary())
		{
			Stack b = sub(s);
			Stack a = sub(s);

			str = "("+infix(a)+tk.getContent()+infix(b)+")";
		}
		else
		if (tk.isNumber())
		{
			str = tk.getContent();
		}
		else
		{
			Stack a = sub(s);
			str = "("+tk.getContent()+" "+infix(a)+")";
		}

		return str;

	}
}

class Token
{
	//The order of these tokens matter and are often hard coded into the code.
	//take care not to disturb it.
	public static String[] validString =
	{
		"0","1","2","3","4","5","6","7","8","9",".",
		"(", ")", "^", "*", "/", "+", "-",
		"sinh", "cosh", "tanh", "csch", "sech", "coth",
		"asinh", "acosh", "atanh", "acsch", "asech", "acoth",
		"sin", "cos", "tan", "csc", "sec", "cot",
		"asin", "acos", "atan",	"acsc", "asec", "acot",
		"sqrt", "neg",
		"log", "abs", "ln", "exp", "sign",
		"ddx",
		"rnd","x","PI", "E"
	};

	public int index=-1;
	public String content="";
	boolean numerical;
	int length;
	
	
	Token(String str)
	{
		String t = str.trim().toLowerCase();
		numerical = false;

		for (int i = 0; i=0 && index<11) index=0;	//Makes sure all numerical values have index 0
	}

	public boolean is(String a)
	{
		return getContent().equalsIgnoreCase(a);
	}

	public boolean isValid()
	{
		return index!=-1;
	}

	public boolean isNumber()
	{
		return (numerical);
	}

	public boolean isOperation()
	{
		return (precedence()>0);
	}

	public boolean isBinary()
	{
		return (precedence()==4 || precedence()==3 || precedence()==2);
	}

	public String getContent()
	{
		return content;
	}

	public float precedence()
	{
		float order=-1;
		Token tk = this;

		String tk_str = tk.getContent().toLowerCase();

		if (tk_str.equals("("))
		{
			order = 5;
		}
		else if (tk_str.equals(")"))
		{
			order = 6;
		}
		else if (tk_str.equals("+"))
		{
			order = 4;
		}
		else if (tk_str.equals("-"))
		{
			order = 4;
		}
		else if (tk_str.equals("*"))
		{
			order = 3;
		}
		else if (tk_str.equals("/"))
		{
			order = 3;
		}
		else if (tk_str.equals("^"))
		{
			order = 2;
		}
		else if (
			tk_str.equals("sin") ||		//Trig functions
			tk_str.equals("cos") ||
			tk_str.equals("tan") ||
			tk_str.equals("csc") ||
			tk_str.equals("sec") ||
			tk_str.equals("cot") ||

			tk_str.equals("asin") ||	//Inverse trig functions
			tk_str.equals("acos") ||
			tk_str.equals("atan") ||
			tk_str.equals("acsc") ||
			tk_str.equals("asec") ||
			tk_str.equals("acot") ||

			tk_str.equals("sinh") ||	//Hyperbolic trig functions
			tk_str.equals("cosh") ||
			tk_str.equals("tanh") ||
			tk_str.equals("csch") ||
			tk_str.equals("sech") ||
			tk_str.equals("coth") ||

			tk_str.equals("asinh") ||	//Inverse hyperbolic trig functions
			tk_str.equals("acosh") ||
			tk_str.equals("atanh") ||
			tk_str.equals("acsch") ||
			tk_str.equals("asech") ||
			tk_str.equals("acoth") ||

			tk_str.equals("ddx") ||		//Derivative

			tk_str.equals("neg") ||		//Negation
			tk_str.equals("sqrt") ||	//Square root
			tk_str.equals("exp") ||		//Exponential base e
			tk_str.equals("ln") ||		//Natural log
			tk_str.equals("log") ||		//Common log
			tk_str.equals("abs") ||		//Absolute value
			tk_str.equals("sign")		//sign
			)
		{
			order=1;
		}
		else if (tk.isNumber())
		{
			order = 0;
		}
		else
		{
			order =-1;
		}

		return order;
	}
	
	public String getValue(String s)
	{
		String ret = "";
		char[] input = s.toLowerCase().toCharArray();
		int state = 1;
		int i = 0;
		
		StackDoub st = new StackDoub();
		
		while (state<30)
		{
			st.push(state);
//			System.out.println(state+" "+input[i]);
			
			switch (state)
			{
				case 1:
					if (Character.isDigit(input[i]))
						state = 2;
					else
					if (input[i]=='.')
						state = 6;
					else
						state = 99;
					break;

				case 2:
					if (Character.isDigit(input[i]))
						state = 2;
					else
					if (input[i]=='e')
						state = 3;
					else
					if (input[i]=='.')
						state = 7;
					else
					{
						i = i - 1;
						state = 99;
					}
					break;

				case 3:
					if (Character.isDigit(input[i]))
						state = 5;
					else
					if (input[i]=='e')
					{
						i = i - 2;
						st.pop();
						state = 99;
					}
					else
					if (input[i]=='.')
						state = 8;
					else
					if (input[i]=='+' || input[i]=='-')
						state = 4;
					else
						state = 99;
					break;

				case 4:
					if (Character.isDigit(input[i]))
						state = 5;
					else
					if (input[i]=='e')
					{
						i = i - 3;
						st.pop();
						st.pop();
						state = 99;
					}
					else
					if (input[i]=='.')
					{
						i = i - 3;
						st.pop();
						st.pop();
						state = 99;
					}
					else
					if (input[i]=='+' || input[i]=='-')
					{
						i = i - 3;
						st.pop();
						st.pop();
						state = 99;
					}
					else
						state = 99;
					break;

				case 5:
					if (Character.isDigit(input[i]))
						state = 5;
					else
					if (input[i]=='.')
						state = 8;
					else
					{
						i = i - 1;
						state = 99;
					}
					break;

				case 6:
					if (Character.isDigit(input[i]))
						state = 7;
					else
					if (input[i]=='e')
						state = 8;
					else
					if (input[i]=='.')
						state = 8;
					else
					if (input[i]=='+' || input[i]=='-')
						state = 8;
					else
						state = 99;
					break;

				case 7:
					if (Character.isDigit(input[i]))
						state = 7;
					else
					if (input[i]=='e')
						state = 3;
					else
					if (input[i]=='.')
						state = 8;
					else
					{
						i = i - 1;
						state = 99;
					}
					break;

				case 8:
					break;
			}
			
			if (i==(input.length-1) || state==99)
			{
				if (state == 99)
					state = (int) st.pop();
			
				if (state!=2 && state!=7 && state != 5)
					state=8;
				else
					state = 99;
			}
			
			if (state == 8)
			{
				s = "?";
				i=0; 
				state = 99;
			}

			i++;
		}
		
		length = i;
		
		ret = s.substring(0,length);
		
		return ret;
		
	}

	public int length()
	{
		return length;
	}
}


class Stack
{
	StackCell top;

	Stack()
	{
		top = null;
	}

	public void clear()
	{
		top = null;
	}

	public boolean isEmpty()
	{
		return top == null;
	}

	public void push(Token s)
	{
		StackCell newCell =  new StackCell(s, top);
		top = newCell;
	}

	public void push(String s)
	{
		push(new Token(s));
	}

	public void push(Stack s)
	{
		Stack t = new Stack();
		if (s==null) return;
		while (! s.isEmpty())
			t.push(s.pop());

		while (! t.isEmpty())
		{
			push(t.peek());
			s.push(t.pop());
		}
	}

	public Token pop()
	{
		if (top!=null)
		{
			Token result = top.now;
			top = top.next;
			return result;
		}

		return new Token("?");
	}

	public Token peek()
	{
		return top.now;
	}
/*
	public static void showAll(Stack s0)
	{
		Stack s1 = new Stack();
		while (! s0.isEmpty())
		{
			Token tk = s0.pop();
			s1.push(tk);
		}

		while (! s1.isEmpty())
		{
			s0.push(s1.pop());
		}
	}
*/
	public String showAll()
	{
		Stack s1 = new Stack();
		String ret="";
		while (! this.isEmpty())
		{
			Token tk = this.pop();
			ret = tk.getContent()+" "+ret;
			s1.push(tk);
		}

		while (! s1.isEmpty())
		{
			this.push(s1.pop());
		}

		return ret;
	}

	public Stack copy ()
	{
		Stack s1 = new Stack();
		Stack s2 = new Stack();
		while (! this.isEmpty())
		{
			s1.push(this.pop());
		}
		while (! s1.isEmpty())
		{
			s2.push(new Token(s1.peek().getContent()));
			this.push(s1.pop());
		}

		return s2;
	}

	public Stack flip ()
	{
		Stack s1 = new Stack();
		Stack s2 = new Stack();
		while (! this.isEmpty())
		{
			s1.push(this.peek());
			s2.push(this.pop());
		}
		while (! s1.isEmpty())
		{
			this.push(s1.pop());
		}

		return s2;
	}

	public boolean isConstant()
	{
		Stack s1 = new Stack();
		boolean isconstant = true;

		while (! isEmpty())
		{
			Token tk = pop();
			s1.push(tk);

			if (tk.is("x")) isconstant = false;
		}

		while (! s1.isEmpty())
			push(s1.pop());

		return isconstant;
	}

	public boolean isZero()
	{
		Postfix p = new Postfix(this);
		return (this.isConstant() && Math.abs(p.evaluate(10))<1e-20);
	}

	public boolean isOne()
	{
		Postfix p = new Postfix(this);
		return (this.isConstant() && Math.abs(p.evaluate(10)-1)<1e-20);
	}

	public boolean isNegativeOne()
	{
		Postfix p = new Postfix(this);
		return (this.isConstant() && Math.abs(p.evaluate(10)+1)<1e-20);
	}

}

	class StackCell
	{
		Token now;
		StackCell next;

		StackCell(Token a, StackCell b)
		{
			now = a;
			next = b;
		}
	}



class StackDoub
{
	java.util.Stack stack;

	StackDoub()
	{
		stack = new java.util.Stack();
	}

	public boolean isEmpty()
	{
		return stack.empty();
	}

	public void push(double s)
	{
		stack.push(new Double(s));
	}

	public double pop()
	{
		return ((Double) stack.pop()).doubleValue();
	}

	public double peek()
	{
		return ((Double) stack.peek()).doubleValue();
	}
}

class PostfixList
{
	Vector List;
	int listSize;

	PostfixList()
	{
		List = new Vector();
		listSize=0;
	}

	void showInfix()
	{
		System.out.println("\n-----------------");
		System.out.println("Current Functions");
		System.out.println("-----------------");

		if (listSize==0)
		{
			System.out.println("None");
		}
		else
		{
			int i=0;
			for (int k=listSize; k>0; i++)
			{
				String s = get(i).infixStr.trim();

				if (s.length()>0)
				{
					System.out.println("F"+i+"(x)="+s);
					k--;
				}

			}
		}
	}

	void add(Postfix pf, Color c)
	{
		boolean placed=false;

		if (listSize100)
			v.removeElementAt(100);
	}

	String getPrev()
	{
		cursor = (cursor+1)%v.size();

		return (String) v.elementAt(cursor);
	}

	String getNext()
	{
		cursor = (cursor-1+v.size())%v.size();

		return (String) v.elementAt(cursor);
	}

}


class Frame extends java.awt.Frame implements WindowListener
{
	Frame()
	{
		super();
	}

	Frame(String title)
	{
		super(title);
	}


	public void windowActivated(WindowEvent e) {}

	public void windowClosed(WindowEvent e) {}

	public void windowClosing(WindowEvent e)
	{
		dispose();
		System.exit(0);
	}

	public void windowDeactivated(WindowEvent e) {}

	public void windowDeiconified(WindowEvent e) {}

	public void windowIconified(WindowEvent e) {}

	public void windowOpened(WindowEvent e) {}
}