//	Main class
//

/*  
 *	Taylor Polynomial Approximation Applet
 *	By Mike May, S.J., August 2001
 *	e-mail: maymk@slu.edu
 *
 *	Some of the work derives from the Riemann Sum applet
 *	by Brendon Cheves & Bill Ziemer
 *  e-mail: brendon@csulb.edu  wziemer@csulb.edu
 *  expr package Copyright 1996 by Darius Bacon 
 */
// Last update 8/2/2001
/*
 *	The applet graphs a function and the specified Taylor polynomial approximation
 *	over a specified range.  The derivatives needed for the Taylor polynomial are 
 *	approximated with standard numerical techniques.  The user can specify the
 *	degree of the approximation be from 0 to 4.
 *
 *	The function can be tyoed in by the user.  The user can also specify the x and
 *	y range of the graph, the base point X0, the approximated point X1, and the 
 *	dX used for the numericalderivative approxiamtions.  The vlaues of X0 and X1
 *	can be changed by mouse click or mouse drag.
 *	
 *  The structure of the program has a main class AproxPoly.  
 *  The work is broken into a number of visible classes:
 *  DrawingPanel, FeedbackPanel, HelpArea, and AboutArea.
 *  The names of these classes are fairly self descriptive.
 *	MultilineLabel is used by AboutArea.
 *  The function being studied is handled by the ZFunction class.
 *  This class uses a parser that calls on the following classes:
 *  Expr, Parser, Syntax_error, Test, TrivialClass and Variable.
 */
/*  
 * public class ApproxPoly extends Applet
 *	public void init()
 *	public boolean action(Event e, Object arg)
 *
 *  The applet is set up in a form that can be modified for other applets.
 *	The main applet has a title label and buttons to switch between three
 *	cards, the main card, the help card, and the about card.
 *	The about card provides author information.
 *	The help card gives help that a user may need to run the applet.
 *	The main card is what we might think of as the applet proper.  It
 *	contains a drawing panel and a feedback panel.  The drawing panel
 *	and feedback panel are separate objects where most of the real work is done.
 *
 *	The init method sets up the basic interface though the most of the hard
 *	details are in the feedback panel and the drawing panel.
 *
 *	An action method is needed to deal with the buttons that switch cards.
 *
 *
 *	public void fixMain()
 *	public void moveFeedbackPanelValues()
 *
 *	The majority of the work is done with the fixMain method.  A subsidiary method
 *	that is called is moveFeedbackPanelValues.
 *
 *
 *	public void drawingPanelClick(double xReal)
 *
 *	The method drawingPanelClick updates things in response to a mouseclick
 *	somewhere on the drawingPanel.
 *
 */

// java packages
import java.applet.*;
import java.awt.*;
import java.lang.Math;
import expr.*;			// f(x) parser by Darius Bacon

public class ApproxPoly extends Applet
{
//  Declare variables

	int	 	drawingPanelWidth = 680, drawingPanelHeight = 340,
			leftXOffset = 40, rightXOffset = 60,
			areaXWidth = drawingPanelWidth - leftXOffset - rightXOffset,
			YOFFSET = 20;
	int		firstIndex = 1, clickIndex = 0;

	double  leftX, rightX;
	double  xMin = 0.0, xMax = 1.0, x0, x1, fX1, approxX1;
	double	zeroDeriv, firstDeriv, secondDeriv, thirdDeriv, fourthDeriv;
	String	functionString, approxString;

//	GUI elements   		
	Button 			helpButton, aboutButton, okBtn;
	Label 			titleLabel;
    CardLayout		theCards;    	
    Panel	 		mainCd, mainPanel, cardChoicePanel;
    HelpArea		helpText;
    AboutArea		aboutText;
    FeedbackPanel	feedbackPanel;
   	DrawingPanel 	theDrawingPanel;

    public void init()
    {
		this.setLayout(null);
		resize(700,500);
  	   	
//	Set up the pieces
		titleLabel = new Label("Taylor Polynomial Approximations", Label.CENTER);
		setLayout(new BorderLayout(3,3));
		this.add(titleLabel, BorderLayout.NORTH);
		titleLabel.setFont(new Font("Dialog", Font.BOLD, 18));
   	   
   	    mainPanel = new Panel();
   	    this.add(mainPanel, BorderLayout.CENTER);
    	// create a card layout and 3 cards
    	theCards = new CardLayout(0,0);
		mainPanel.setLayout(theCards);
 		// create the main card
 		mainCd = new Panel();
 		mainPanel.add("main",mainCd);
 		mainCd.setLayout(new BorderLayout(3,3));
 		// create the about card
		aboutText = new AboutArea();
   	    aboutText.setBounds(0,0,600,380);
 		mainPanel.add("about",aboutText);
 		// create the help card
		helpText = new HelpArea(600, 300);
   	    helpText.setBounds(0,0,600,380);
 		mainPanel.add("help",helpText);
  
		// create the feedback panel and add it to the main cd
 		feedbackPanel = new FeedbackPanel(this);
   	    feedbackPanel.setSize(200,400);
		mainCd.add(feedbackPanel, BorderLayout.EAST);
		feedbackPanel.fixInputs();
		feedbackPanel.resetYMinYMax();		

    	// create the drawing panel and add it to the main cd
   		theDrawingPanel = new DrawingPanel(this);
		mainCd.add(theDrawingPanel, BorderLayout.CENTER);
		fixMain();
 		
	/**
	 *	Add a panel to the bottom of the screen to let the user choose
	 *	between cards.
	 */
   	    cardChoicePanel = new Panel();
   	    this.add(cardChoicePanel, BorderLayout.SOUTH);
		cardChoicePanel.setLayout(new GridLayout(1,0,10,0));
		okBtn = new Button("Main Panel");
		cardChoicePanel.add(okBtn);
		helpButton = new Button("Help Panel");
		cardChoicePanel.add(helpButton);
		aboutButton = new Button("About Panel");
		cardChoicePanel.add(aboutButton);

		// finally, show the main card
		theCards.show(mainPanel,"main");

    }   // init
    
 	// handle GUI events
    public boolean action(Event e, Object arg)
	{
		double outputdx;
     	if(e.target == aboutButton)
      	{
			theCards.show(mainPanel,"about");
      		return true;
      	}
      	else if(e.target == helpButton)
      	{
			theCards.show(mainPanel,"help");
      		return true;
      	}
      	else if(e.target == okBtn)
      	{
			theCards.show(mainPanel,"main");
      	    feedbackPanel.fixInputs();
      	    fixMain();
        	paint(theDrawingPanel.getGraphics());
	   		return true;
      	}

 		else return super.action(e,arg);	
	}  //  action

    public void fixMain()
    {
		feedbackPanel.updateFeedbackPanelValues();
		moveFeedbackPanelValues();  
		theDrawingPanel.updateDrawingValues();
	   	paint(getGraphics());
    	theDrawingPanel.repaint();
    }	//	fixMain
        
    public void moveFeedbackPanelValues()
    {
 		theDrawingPanel.functionString	= feedbackPanel.functionString;
 		theDrawingPanel.xBase 			= feedbackPanel.xBase;
 		theDrawingPanel.xDel 			= feedbackPanel.xDel;
 		theDrawingPanel.x1 				= feedbackPanel.x1;
 		theDrawingPanel.xMin 			= feedbackPanel.xMin;
 		theDrawingPanel.xMax			= feedbackPanel.xMax;
 		theDrawingPanel.bottomY 		= feedbackPanel.yMin;
 		theDrawingPanel.topY			= feedbackPanel.yMax;
  		theDrawingPanel.firstIndex 		= feedbackPanel.firstIndex;
 		clickIndex 						= feedbackPanel.clickIndex;
 		theDrawingPanel.clickIndex 		= feedbackPanel.clickIndex;
   }	//	importfeedbackPanelValues
    


	public void drawingPanelClick()
	{
		feedbackPanel.setXBase(theDrawingPanel.xBase);
		feedbackPanel.setX1(theDrawingPanel.x1);
		if(clickIndex == 2) 
		{
			feedbackPanel.yMin = theDrawingPanel.bottomY;
			feedbackPanel.yMax = theDrawingPanel.topY;
			feedbackPanel.xMin = theDrawingPanel.leftX;
			feedbackPanel.xMax = theDrawingPanel.rightX;
			feedbackPanel.postXMinXMax();
			feedbackPanel.postYMinYMax();
		}
		feedbackPanel.fixInputs();
		fixMain();
	}	//	drawingPanelClick


}	//	ApproxPoly

////
////
////
//		Other pieces
////
////

/*
 *	The drawing panel is an object for the graphical display of the applet.
 *
 *
 *public class DrawingPanel extends Canvas implements MouseListener, MouseMotionListener
 *	public  DrawingPanel(ApproxPoly appPoly, ZFunction f)
 *	public void updateDrawingValues()
 *	public void repaint()
 *	public void paint(Graphics g)
 *  public void mouseDragged(MouseEvent e) 
 *  public void mouseMoved(MouseEvent e) 
 *  public void mousePressed(MouseEvent e)
 *  public void mouseReleased(MouseEvent e)
 *  public void mouseEntered(MouseEvent e)
 *  public void mouseExited(MouseEvent e)
 *  public void mouseClicked(MouseEvent e)
 *
 *	The constructor does little more than extend canvas and identify the applet
 *	and applet so that information can be passed back and forth.  It also adds
 *	action listeners for the mouse.  The corresponding methods are included,
 *	though all but mouse dragged and mouse releases are empty.  The repaint method
 *	simply calls paint which does all the work.  The updateDrawingValues method
 *	computes values used by the drawingPanel after data has been transferred from
 *	the feedbackPanel.
 *
 *
 *	public boolean mouseUp(Event e, int x, int y) 
 *
 *	This method listens for mouse clicks on the graph and starts appropriate action
 *
 *
 *	public void plot(Graphics g, ZFunction f, double leftX, double rightX, Color color)
 *
 *	This methods draws in the function or the appropriate approximation.
 *
 *
 *	public void drawAxis(Graphics g)
 *	public void drawLimits(Graphics g)
 *
 *	These methods add in the axes and the text that goes on the graph.  
 *
 *
 *	public double xPixelToReal(double xPixel)
 *	public double xPixelToReal(int xPixel)
 *	public int xRealToPixel(double xReal)
 *	public int xRealToPixel(int xReal)
 *	public int yRealToPixel(double yReal)
 *	public int yRealToPixel(int yReal)
 *	public double yPixelToReal(double yPixel)
 *	public double yPixelToReal(int yPixel)
 *
 *	These methods switch between coordinate and pixel measurements of locations on 
 *	the graph
 *	
 */


import java.awt.event.*;
import java.awt.*;
import java.applet.*;
import MIO.*;

public class DrawingPanel extends Canvas implements MouseListener, MouseMotionListener
{
//  Declare variables
//	variables concerned with the size of the panel and how much is used 
//	for the actual drawing.  
//	The left and right offsets determine where
//	the Riemann approximation is done.  The vertical offset is just to 
//	make everything look nice.  The panel is set up with a single offset
//	at the top and a double one at the bottom of the panel.
	int	 	drawingPanelWidth = 680, drawingPanelHeight = 340,
			leftXOffset = 40, rightXOffset = 60,
			areaXWidth = drawingPanelWidth - leftXOffset - rightXOffset,
			YOFFSET = 20;
//	variables concerning the approximation to be graphed
	int		firstIndex = 0, clickIndex = 0;	
	double	xBase=-1.0, x1=1.0;
	double  leftX, rightX, bottomY, topY, halfHigh, halfWide;
	double  xMin = 0.0, xMax = 1.0, xDel;
	String	functionString = "x+0.1*x^2-3*sin(x)", approxString;
	double	approxX1, fX1, zeroDeriv;
   		
	ZFunction	theFunction, approxFunction;
	ApproxPoly	applet;


	public  DrawingPanel(ApproxPoly appPoly)
	{
		super();		
  	   	theFunction = new ZFunction();
  	   	approxFunction = new ZFunction();
		applet = appPoly;
		addMouseMotionListener(this);
		addMouseListener(this);
	}	//	DrawingPanel	constructor
	
	public void repaint()
	{
		paint(getGraphics());
	}	//	repaint
	
	public void updateDrawingValues()
	{
		drawingPanelWidth = getSize().width;
		drawingPanelHeight = getSize().height;
		areaXWidth = drawingPanelWidth - leftXOffset - rightXOffset;
	    theFunction.parse(functionString);
 		fX1 = theFunction.value(x1);
		theFunction.xBase 		= xBase;
    	theFunction.xDel 		= xDel;
	    theFunction.findNumDerivs();
	    zeroDeriv = theFunction.zeroDeriv;
		leftX 	= xMin - (1.0*leftXOffset/areaXWidth)*(xMax-xMin);
		rightX	= xMax + (1.0*rightXOffset/areaXWidth)*(xMax-xMin);

		approxString = Mio.doubleToStringNoE(theFunction.zeroDeriv);
		if(firstIndex >= 1) approxString = approxString + " + " 
			+ Mio.doubleToStringNoE(theFunction.firstDeriv) + "*(x-" + Mio.doubleToStringNoE(xBase) +")";
		if(firstIndex >= 2) approxString = approxString + " + " 
			+ Mio.doubleToStringNoE(theFunction.secondDeriv) + "*(x-" + Mio.doubleToStringNoE(xBase) +")^2/2 ";
		if(firstIndex >= 3) approxString = approxString + " + " 
			+ Mio.doubleToStringNoE(theFunction.thirdDeriv) + "*(x-" + Mio.doubleToStringNoE(xBase) +")^3/6 ";
		if(firstIndex >= 4) approxString = approxString + " + " 
			+ Mio.doubleToStringNoE(theFunction.fourthDeriv) + "*(x-" + Mio.doubleToStringNoE(xBase) +")^4/24 ";
		approxFunction.parse(approxString);
	}
	

	public void paint(Graphics g)
	{
		updateDrawingValues();
	//	clear the canvas
		g.clearRect(0,0,drawingPanelWidth,drawingPanelHeight);
	//	plot the function		
		plot(g, theFunction, leftX, rightX, Color.cyan);
	//	paint the appropriate approximation
		if(firstIndex == 0)plot(g, approxFunction, leftX, rightX, Color.magenta);
		if(firstIndex == 1)plot(g, approxFunction, leftX, rightX, Color.green);
		if(firstIndex == 2)plot(g, approxFunction, leftX, rightX, Color.red);
		if(firstIndex == 3)plot(g, approxFunction, leftX, rightX, Color.black);
		if(firstIndex == 4)plot(g, approxFunction, leftX, rightX, Color.blue);
	//	add axes and limits		
		drawAxis(g);
 		drawLimits(g);
	}	//	paint

	//	methods for the mouse listener and the mouse motion listener
    public void mouseDragged(MouseEvent e) 
    {
  		if(clickIndex < 2)
  		{
  		double 	xRealClick = xPixelToReal(e.getX());
  		double 	yRealClick = yPixelToReal(e.getY());
  		double	xReal2 = ((int)(xRealClick*1000))/1000.0;
  		double	yReal2 = ((int)(yRealClick*1000))/1000.0;
  		if (clickIndex == 0) xBase = xReal2;
  		if (clickIndex == 1) x1 = xReal2;
  		applet.drawingPanelClick();
  		}      
    }	//	mouseDragged

    public void mouseMoved(MouseEvent e) 
    {
    }	//mouseMoved

    public void mousePressed(MouseEvent e) 
    {
    }	//	mousePressed

    public void mouseReleased(MouseEvent e) 
    {
   		double 	xRealClick = xPixelToReal(e.getX());
  		double 	yRealClick = yPixelToReal(e.getY());
  		double	xReal2 = ((int)(xRealClick*1000))/1000.0;
  		double	yReal2 = ((int)(yRealClick*1000))/1000.0;
  		if (clickIndex == 0) xBase = xReal2;
  		if (clickIndex == 1) x1 = xReal2;
  		if (clickIndex == 2)
  		{
	  		halfHigh = (topY - bottomY)/2;
	  		topY = yReal2 + halfHigh;
	  		bottomY = yReal2 - halfHigh;
	  		halfWide = (xMax - xMin)/2;
	  		rightX = xReal2 + halfWide;
	  		leftX = xReal2 - halfWide;
  		}
  		applet.drawingPanelClick();      
	}	//	mouseReleased

    public void mouseEntered(MouseEvent e) {
    }	//	mouseEntered	

    public void mouseExited(MouseEvent e) {
    }	//	mouseExited

    public void mouseClicked(MouseEvent e) {
    }	//	mouseClicked
	//	end of motion litener methods	

	public void plot(Graphics g, ZFunction f, double leftX, double rightX, Color color)
	{
		int y1, y2, j;

      	g.setColor(color);
		
		f.xvar.set_value(xPixelToReal(leftX));
		y1 = yRealToPixel(f.expr.value());
		
		for(int i=xRealToPixel(leftX); i < xRealToPixel(rightX); i++)
		{
			f.xvar.set_value(xPixelToReal(i+1));
			y2 = yRealToPixel(f.expr.value());
			// Bug in drawLine that freaks when both arguments 
			//  outside drawing area; yields spurious vertical lines
			if( (0 <= y2) && (y2 <= drawingPanelHeight)) 
			{	g.drawLine(i, y1, i+1, y2);
			}			
			y1 = y2;
		}
	}  //  plot
		
	// draw the axis
	public void drawAxis(Graphics g)
	{
	    int xAxisPixel, yAxisPixel;
       	int x1Pixel = (int) xRealToPixel(x1);
       	
	  	g.setColor(Color.black);
 	  	
 	  	xAxisPixel = yRealToPixel(0);
  	  	if(xAxisPixel > drawingPanelHeight - 50) xAxisPixel = drawingPanelHeight - 50;
 	  	if(xAxisPixel < 28) xAxisPixel = 28;
 	  	yAxisPixel = leftXOffset;
		
		// x axis
        g.drawLine(0,xAxisPixel,drawingPanelWidth - 20,xAxisPixel);	
 
        // y axis
        g.drawLine(yAxisPixel,20,
                       yAxisPixel,drawingPanelHeight-10);
        
        g.drawString("X",drawingPanelWidth - 15,xAxisPixel + 4);
        g.drawString("Y",yAxisPixel-2,17);

		//	approximating function
 	  	g.setColor(Color.black);
        g.drawLine(x1Pixel,20,
                       x1Pixel,drawingPanelHeight-10);
		

 	}  //  drawAxis

	// draw limits in the proper places
	public void drawLimits(Graphics g)
	{
      	int xAxisPixel, yAxisPixel, xMaxPixel, xMidPixel, yPixel;
      	int xBasePixel, x1Pixel;
      	double yVal;
      	xBasePixel = (int) xRealToPixel(xBase);
      	x1Pixel = (int) xRealToPixel(x1);
      	xAxisPixel = (int) yRealToPixel(0);
 	  	if(xAxisPixel > drawingPanelHeight - 50) xAxisPixel = drawingPanelHeight - 50;
 	  	if(xAxisPixel < 28) xAxisPixel = 28;
      	yAxisPixel = leftXOffset;
      	xMidPixel  = leftXOffset + areaXWidth/2;
      	xMaxPixel  = leftXOffset + areaXWidth;
      	g.setColor(Color.red);
		
		// x limit
		g.drawString(Mio.doubleToStringSigFigures(xMin, 4), yAxisPixel + 4,xAxisPixel + 14);
		g.drawLine(yAxisPixel,xAxisPixel - 2, yAxisPixel,xAxisPixel + 2);

		g.drawString(Mio.doubleToStringSigFigures((xMin + xMax)/2.0, 4), xMidPixel + 4,xAxisPixel + 14);
		g.drawLine(xMidPixel,xAxisPixel - 2, xMidPixel,xAxisPixel + 2);

		g.drawString(Mio.doubleToStringSigFigures(xMax, 4), xMaxPixel + 4,xAxisPixel + 14);
		g.drawLine(xMaxPixel,xAxisPixel - 2, xMaxPixel,xAxisPixel + 2);
		
		// y limit
		yVal = topY;
		yPixel = yRealToPixel(yVal);
		g.drawString(Mio.doubleToStringSigFigures(yVal, 4), yAxisPixel - 40,yPixel + 3);
		g.drawLine(yAxisPixel - 2,yPixel, yAxisPixel + 2,yPixel);
		
		yVal = bottomY;
		yPixel = yRealToPixel(yVal);
		g.drawString(Mio.doubleToStringSigFigures(yVal, 4), yAxisPixel - 40,yPixel + 3);
		g.drawLine(yAxisPixel - 2,yPixel, yAxisPixel + 2,yPixel);
		
		//	(X0, f(X0))
		g.drawString(Mio.doubleToStringSigFigures(xBase, 4), xBasePixel + 4,xAxisPixel + 14);
		g.drawString("X0", xBasePixel + 4,xAxisPixel + 28);
		g.drawLine(xBasePixel,xAxisPixel - 2, xBasePixel,xAxisPixel + 2);
		yVal = theFunction.value(xBase);
		yPixel = yRealToPixel(yVal);
		g.drawString(Mio.doubleToStringSigFigures(yVal, 4), yAxisPixel - 40,yPixel + 3);
		g.drawLine(yAxisPixel - 2, yPixel, yAxisPixel + 2, yPixel);
		g.drawLine(xBasePixel - 2, yPixel, xBasePixel + 2, yPixel);
		g.drawLine(xBasePixel,yPixel - 2, xBasePixel,yPixel + 2);
		
		//	(X1, f(X1))
		g.drawString(Mio.doubleToStringSigFigures(x1, 4), x1Pixel + 4,xAxisPixel -16);
		g.drawString("X1", x1Pixel + 4, xAxisPixel -2);
		g.drawLine(x1Pixel, xAxisPixel - 2, x1Pixel, xAxisPixel + 2);
		yVal = theFunction.value(x1);
		yPixel = yRealToPixel(yVal);
		g.drawString(Mio.doubleToStringSigFigures(yVal, 4), yAxisPixel - 40,yPixel + 3);
		g.drawLine(yAxisPixel - 2, yPixel, yAxisPixel + 2, yPixel);
		g.drawLine(x1Pixel - 2, yPixel, x1Pixel + 2, yPixel);
		
  	}  //  drawLimits
		
	// input pixel, output real value in interval [xMIn, xMax]
	public double xPixelToReal(double xPixel)
	{
		return ((xPixel - leftXOffset) * (xMax - xMin) / 
		            areaXWidth + xMin);
	}  //  xPixelToReal
	
	public double xPixelToReal(int xPixel)
	{
		return ((xPixel - leftXOffset) * (xMax - xMin) / 
		            areaXWidth + xMin);
	}  //  xPixelToReal


	// input real, output pixel in interval [0, XSCALE]
	public int xRealToPixel(double xReal)
	{
		return (int)((xReal - xMin) / 
		              (xMax - xMin) * areaXWidth + leftXOffset);
	}  //  xRealToPixel

	public int xRealToPixel(int xReal)
	{
		return (int)((xReal - xMin) / 
		              (xMax - xMin) * areaXWidth + leftXOffset);
		
	}  //  xRealToPixel

	// input real value, output pixel in interval [0, YSCALE]
	public int yRealToPixel(double yReal)
	{
		return (int)((yReal - topY) * (drawingPanelHeight - 3.0*YOFFSET) / 
		         (bottomY - topY) + YOFFSET);
	}  //  yRealToPixel

	public int yRealToPixel(int yReal)
	{
		return (int)((yReal - topY) * (drawingPanelHeight - 3.0*YOFFSET) / 
		        (bottomY - topY) + YOFFSET);
	}  //  yRealToPixel


	// input pixel, output real in interval [yMin, yMax]
	public double yPixelToReal(double yPixel)
	{
		return ((yPixel - YOFFSET) * (bottomY - topY) / 
		        (drawingPanelHeight - 3*YOFFSET) + topY);
	}  //  yPixelToReal

	public double yPixelToReal(int yPixel)
	{
		return ((yPixel - YOFFSET) * (bottomY - topY) / 
		        (drawingPanelHeight - 3*YOFFSET) + topY);
	}  //  yPixelToReal

}	//	DrawingPanel class

/*
 *	The feedback panel is intended to contain all the inputs and text
 *	outputs used by the applet.  
 *
 *
 *	public FeedbackPanel(ApproxPoly applet)
 *	public void setChoices(Choice currentChoice)
 *	class SymItem implements java.awt.event.ItemListener
 *  public boolean action(Event e, Object arg)
 *
 *	The longest method of the class is the constructor that puts the GUI 
 *	interface into place.  In this case setChoices is a subsidiary method 
 *	used to set up the choices on drop down menues.
 *
 *	The action method and the itemStateChanged method listen for button 
 *	pushes, return in a text field, and choosing an item from a menu.
 *	Each button causes its own results.
 *
 *
 *
 *	public void fixInputs()
 *  public void updateFeedbackPanelValues()
 *	public void resetYMinYMax()
 *  public void getXBase()
 *  public void getX1()
 *  public void getXDel()
 *  public void getXMinXMax()
 *  public void getYMinYMax()
 *
 *  public void postXMinYMax()
 *  public void postYMinYMax()
 *
 *	public void drawLabels()
 *  public void setXBase(double xBase)
 *  public void setX1(double x1)
 *
 *	The two main methods fixInputs and drawLabels collect input and print
 *	out the output.
 *
 *	The subsidiary methods used in collecting data are getXminXMax,
 *	getXBase, getX1 and getXDel.
 *	postXMinXMax and postYMinYMax put values back in text fields.
 *	Subsidiary methods of printing output are setXBase and setX1.
 *
 */

import java.awt.*;
import java.applet.*;
import MIO.*;

public class FeedbackPanel extends Panel
{
	/*
	 *		Public variables that are either inputs or outputs to be passed
	 */

	double	xBase, xDel, zeroDeriv; 
	double  yMin = -5.0, yMax = 5.0;
	double  xMin = -5.0, xMax = 5.0, x1 = 0.54, fX1, approxX1;
	int		firstIndex, clickIndex;
    String	functionString;
    double	xCenter, halfWide, yCenter, halfHigh;
  
	/*
	 *		GUI elements for the panel
	 */	
	
	Button		graphfx, zoomIn, zoomOut, zSquare;
	Button		yZoomIn, yZoomOut, yReset;
	Choice 		approxChoice, clickChoice;
    Panel		miscPanel,  domainPanel, approxPanel, evalPanel;
    Panel		checkBoxPanel;
	Label 		zeroDerivLabel;
	Label   	fxLabel, xBaseLabel, xDelLabel, 
	 			xMinLabel, xMaxLabel, yMinLabel, yMaxLabel, 
	    		x1Label, fEvalLabel, approxEvalLabel, errorLabel,
	    		approx0Label, approx1Label, approx2Label, approx3Label, approx4Label;
    TextField 	functionFld, xMinField, xMaxField, yMinField, yMaxField, 
    			xBaseField, xDelField, x1Field;
    String		approxString, approxString0, approxString1, approxString2, approxString3,
    			approxString4;

	
	ApproxPoly  approxPoly;
	ZFunction	theFunction, approxFunction;

	public FeedbackPanel(ApproxPoly applet)
	{
		this.setLayout(null);
		approxPoly = applet;
		theFunction = new ZFunction();
		approxFunction = new ZFunction();
   	 /*
   	  *		The feedback panel is further divided into a number of panels.
   	  *
   	  *		miscPanel contains info on the function, the base point, the size of xDel 
   	  *		which is used for numeric approximations of the derivatives and the 
   	  *		graphing domain.  It is 200 by 100 and on the top.
   	  *
   	  *		Thee middle is the approxPanel which contains the approximation 
   	  *		point X1, a choice menu of types of approximation, the value of f and the
   	  *		approximation evaluates at X1, along with the error.  There is also
   	  *		a choice menu to determine if clickiing on the graph panel moves X0 or X1.
   	  *		This panel is 200 by 200.
   	  *
   	  *		On the bottom is the domainPanel which contains info on x and y limits
   	  *		of the graph.  There are buttons for zooming in and zooming out, for
   	  *		squaring the graph region, and for adjusting the graph to the function.
   	  *		This panel is 200 by 60 pixels.
   	  */  	 		
 		miscPanel = new Panel();
 		miscPanel.setBounds(0,0,200,100);
  		miscPanel.setLayout(null);
		this.add(miscPanel);
		fxLabel = new Label("f(x) = ");
		fxLabel.setBounds(0,0,35,20);
		miscPanel.add(fxLabel);
		functionFld = new TextField("x+0.1*x^2-3*sin(x)", 30);
		functionFld.setBounds(0,20,200,20);
		miscPanel.add(functionFld);

		xBaseLabel = new Label("The base point is");
		xBaseLabel.setBounds(0,40,85,20);
		miscPanel.add(xBaseLabel);
		xBaseField = new TextField("1.5", 6);
		xBaseField.setBounds(85,40,60,20);
		miscPanel.add(xBaseField);

		xDelLabel = new Label("del X is");
		xDelLabel.setBounds(0,60,35,20);
		miscPanel.add(xDelLabel);
		xDelField = new TextField("0.01", 6);
		xDelField.setBounds(70,60,50,20);
		miscPanel.add(xDelField);

 		zeroDerivLabel = new Label("f(X0) = ");
		zeroDerivLabel.setBounds(0,80,120,20);
		miscPanel.add(zeroDerivLabel);

   	    domainPanel = new Panel();
   	    domainPanel.setBounds(0, 300, 200, 60);
  		domainPanel.setLayout(null);
		this.add(domainPanel);
 		xMinLabel = new Label("xMin =");
   	    xMinLabel.setBounds(0, 20, 40, 20);
		domainPanel.add(xMinLabel);
		xMinField = new TextField("-5.0", 7);
   	    xMinField.setBounds(45, 20, 50, 20);
		domainPanel.add(xMinField);
		xMaxLabel = new Label("xMax =");
   	    xMaxLabel.setBounds(0, 0, 40, 20);
		domainPanel.add(xMaxLabel);
		xMaxField = new TextField("5.0", 7);
   	    xMaxField.setBounds(45, 0, 50, 20);
		domainPanel.add(xMaxField);
		zoomIn = new Button("Zoom In");
    	zoomIn.setBounds(0,42,45,18);
		domainPanel.add(zoomIn);
 		zoomOut = new Button("Zoom Out");
    	zoomOut.setBounds(50,42,50,18);
		domainPanel.add(zoomOut);

 		yReset = new Button("Reset Y");
    	yReset.setBounds(105,42,45,18);
		domainPanel.add(yReset);
 		zSquare = new Button("Square");
    	zSquare.setBounds(155,42,45,18);
		domainPanel.add(zSquare);

 		yMinLabel = new Label("yMin =");
   	    yMinLabel.setBounds(100, 20, 40, 20);
		domainPanel.add(yMinLabel);
		yMinField = new TextField("-5.0", 7);
   	    yMinField.setBounds(145, 20, 50, 20);
		domainPanel.add(yMinField);
		yMaxLabel = new Label("yMax =");
   	    yMaxLabel.setBounds(100, 0, 40, 20);
		domainPanel.add(yMaxLabel);
		yMaxField = new TextField("5.0", 7);
   	    yMaxField.setBounds(145, 0, 50, 20);
		domainPanel.add(yMaxField);


   	    approxPanel = new Panel();
    	approxPanel.setBounds(0,100,200,200);
 		approxPanel.setLayout(null);
		this.add(approxPanel);
 		x1Label = new Label("X1 = ");
 		x1Label.setBounds(0,0,25,20);
		approxPanel.add(x1Label);
		x1Field = new TextField("2.74", 7);
		x1Field.setBounds(25,0,55,20);
		approxPanel.add(x1Field);
		errorLabel = new Label("error = ");
		errorLabel.setBounds(90,0,105,20);
		approxPanel.add(errorLabel);
		fEvalLabel = new Label("f(X1) = ");
		fEvalLabel.setBounds(0,20,95,20);
		approxPanel.add(fEvalLabel);
		approxEvalLabel = new Label("Apr(X1) = ");
		approxEvalLabel.setBounds(100,20,95,20);
		approxPanel.add(approxEvalLabel);

 		approxChoice = new Choice();
 		approxChoice.addItem("Constant Approx");
 		approxChoice.addItem("Linear Approx");
 		approxChoice.addItem("Quadratic Approx");
 		approxChoice.addItem("Cubic Approx");
 		approxChoice.addItem("Quartic Approx");
		approxChoice.select(1);
		approxChoice.setBounds(0,40,115,20);
		approxPanel.add(approxChoice);

 		clickChoice = new Choice();
 		clickChoice.addItem("X0 click");
 		clickChoice.addItem("X1 click");
 		clickChoice.addItem("Center");
		clickChoice.select(0);
		clickChoice.setBounds(120,40,75,20);
		approxPanel.add(clickChoice);

		SymItem lSymItem = new SymItem();
		approxChoice.addItemListener(lSymItem);
		clickChoice.addItemListener(lSymItem);

 		approx0Label = new Label(" ");
 		approx0Label.setBounds(0,60,190,20);
		approxPanel.add(approx0Label);
 		approx1Label = new Label(" ");
 		approx1Label.setBounds(0,80,190,20);
		approxPanel.add(approx1Label);
 		approx2Label = new Label(" ");
 		approx2Label.setBounds(0,100,190,20);
		approxPanel.add(approx2Label);
 		approx3Label = new Label(" ");
 		approx3Label.setBounds(0,120,190,20);
		approxPanel.add(approx3Label);
 		approx4Label = new Label(" ");
 		approx4Label.setBounds(0,140,190,20);
		approxPanel.add(approx4Label);

	}

// handle GUI events
	//	listen for changes in menues	
	class SymItem implements java.awt.event.ItemListener
	{
		public void itemStateChanged(java.awt.event.ItemEvent event)
		{
      	    fixInputs();
      	    approxPoly.fixMain();
    	}
	}
	
	//	listen for button pushes and returns in text fields
    public boolean action(Event e, Object arg)
	{
      	if((e.target == graphfx)||(e.target == xBaseField)||
      	   (e.target == xMinField)||(e.target == xMaxField)||
      	   (e.target == yMinField)||(e.target == yMaxField)||
      	   (e.target == xDelField)||(e.target == x1Field))
      	{
      	    fixInputs();
			theFunction.yMin = yMin;
			theFunction.yMax = yMax;
	    	approxPoly.fixMain();
      		return true;
      	}
      	
      	if((e.target == functionFld)||(e.target == yReset))
      	{
      	    fixInputs();
		    theFunction.parse(functionString);
			theFunction.determineYMinYMax();
			resetYMinYMax();
      		approxPoly.fixMain();
      		return true;
      	}
      	
   		if(e.target == zoomIn)
      	{
      		xCenter = (xMax + xMin)/2;
      		halfWide = xMax - xCenter;
			xMinField.setText(Double.toString(xCenter - halfWide/2.0));
			xMaxField.setText(Double.toString(xCenter + halfWide/2.0));
       		yCenter = (yMax + yMin)/2;
      		halfHigh = yMax - yCenter;
			yMinField.setText(Double.toString(yCenter - halfHigh/2.0));
			yMaxField.setText(Double.toString(yCenter + halfHigh/2.0));
     	    fixInputs();
      		approxPoly.fixMain();
      		return true;
      	}
   		if(e.target == zoomOut)
      	{
      		xCenter = (xMax + xMin)/2;
      		halfWide = xMax - xCenter;
			xMinField.setText(Double.toString(xMin - halfWide));
			xMaxField.setText(Double.toString(xMax + halfWide));
      		yCenter = (yMax + yMin)/2;
      		halfHigh = yMax - yCenter;
			yMinField.setText(Double.toString(yMin - halfHigh));
			yMaxField.setText(Double.toString(yMax + halfHigh));
      	    fixInputs();
      		approxPoly.fixMain();
      		return true;
      	}
 
   		if(e.target == zSquare)
      	{
      		halfWide = (xMax - xMin)/2;
      		yCenter = (yMax + yMin)/2;
			yMinField.setText(Double.toString(yCenter - halfWide));
			yMaxField.setText(Double.toString(yCenter + halfWide));
      	    fixInputs();
      		approxPoly.fixMain();
      		return true;
      	}

 		else return super.action(e,arg);	
	}  //  action

	
//		Gather the inputs - main collection method	
	public void fixInputs()
    {
			getXBase();
			getX1();
			getXDel();
			getXMinXMax();
			getYMinYMax();
    		functionString = functionFld.getText();
			firstIndex = approxChoice.getSelectedIndex();
			clickIndex = clickChoice.getSelectedIndex();
			updateFeedbackPanelValues();
    }	//	fixInputs
    
    public void updateFeedbackPanelValues()
    {
    	theFunction.parse(functionString);
		theFunction.xBase 		= xBase;
    	theFunction.xDel 		= xDel;
    	fX1 = theFunction.value(x1);
	    theFunction.findNumDerivs();
	    zeroDeriv = theFunction.zeroDeriv;

		approxString0 = "Apr(X) = " + Mio.doubleToStringSigFigures(theFunction.zeroDeriv,5);
		approxString = Mio.doubleToStringNoE(theFunction.zeroDeriv);
		approxString1 = " ";
		if(firstIndex >= 1) 
		{
		approxString1 =  "  + " + Mio.doubleToStringSigFigures(theFunction.firstDeriv,5) 
			+ "*(X - " + Mio.doubleToStringSigFigures(xBase,5) + ")";
		approxString = approxString + " + " 
			+ Mio.doubleToStringNoE(theFunction.firstDeriv) + "*(x-" + Mio.doubleToStringNoE(xBase) +")";
		}
		approxString2 = " ";
		if(firstIndex >= 2) 
		{
		approxString2 =  "  + " + Mio.doubleToStringSigFigures(theFunction.secondDeriv,5) 
			+ "*(X - " + Mio.doubleToStringSigFigures(xBase,5) + ")^2/2 ";
		approxString = approxString + " + " 
			+ Mio.doubleToStringNoE(theFunction.secondDeriv) + "*(x-" + Mio.doubleToStringNoE(xBase) +")^2/2 ";
		}
		approxString3 = " ";
		if(firstIndex >= 3) 
		{
		approxString3 = "  + " + Mio.doubleToStringSigFigures(theFunction.thirdDeriv,5) 
			+ "*(X - " + Mio.doubleToStringSigFigures(xBase,5) + ")^3/6 ";
		approxString = approxString + " + " 
			+ Mio.doubleToStringNoE(theFunction.thirdDeriv) + "*(x-" + Mio.doubleToStringNoE(xBase) +")^3/6 ";
		}
		approxString4 = " ";
		if(firstIndex >= 4) 
		{
		approxString4 = "  + " + Mio.doubleToStringSigFigures(theFunction.fourthDeriv,5)
			+ "*(X - " + Mio.doubleToStringSigFigures(xBase,5) + ")^4/24 ";
		approxString = approxString + " + " 
			+ Mio.doubleToStringNoE(theFunction.fourthDeriv) + "*(x-" + Mio.doubleToStringNoE(xBase) +")^4/24 ";
		}
		approxFunction.parse(approxString);
		approxX1 = approxFunction.value(x1);
		drawLabels();
    }		//	updateFeedbackPanelValues
    
	public void resetYMinYMax()
	{
		    theFunction.parse(functionString);
		    theFunction.xMin = xMin;
		    theFunction.xMax = xMax;
			theFunction.determineYMinYMax();
			yMin = theFunction.yMin;
			yMax = theFunction.yMax;
			postYMinYMax();
	}		//	resetYMinYMax

//		Subsidary methods for collecting data
    public void getXBase()
    {
    xBase = Mio.doubleFromTextField(xBaseField);
    }	//	getXBase
    
    public void getX1()
    {
    x1 = Mio.doubleFromTextField(x1Field);
    }	//	getX1
    
    public void getXDel()
    {
    xDel = Mio.doubleFromTextField(xDelField);
    if (xDel <= 0.0) 
       {
       System.out.println("Warning, del X should be positive");
       System.out.println("del X reset to default.");
       xDel = 0.01;
       xDelField.setText(Double.toString(xDel));
       }
    }	//	getXDel
    
    public void getXMinXMax()
    {
    //  Check that xMin < xMax and set them
    xMin=Mio.doubleFromTextField(xMinField);
    xMax=Mio.doubleFromTextField(xMaxField);
    if (xMax <= xMin) 
       {
       System.out.println("Warning, xMin should be < xMax");
       System.out.println("xMin and xMax reset to defaults.");
       xMin = -5.0;
       xMax = 5.0;
       }
       xMinField.setText(Double.toString(xMin));
       xMaxField.setText(Double.toString(xMax));
    }  //  getXMinXMax

    public void getYMinYMax()
    {
    //  Check that xMin < xMax and set them
    yMin=Mio.doubleFromTextField(yMinField);
    yMax=Mio.doubleFromTextField(yMaxField);
    if (yMax <= yMin) 
       {
       System.out.println("Warning, yMin should be < yMax");
       System.out.println("xMin and xMax reset to defaults.");
       yMin = -5.0;
       yMax = 5.0;
       }
       yMinField.setText(Double.toString(yMin));
       yMaxField.setText(Double.toString(yMax));
    }  //  getYMinYMax

    public void postXMinXMax()
    {
       xMinField.setText(Mio.doubleToStringSigFigures(xMin,5));
       xMaxField.setText(Mio.doubleToStringSigFigures(xMax,5));
    }  //  postYMinYMax

    public void postYMinYMax()
    {
       yMinField.setText(Mio.doubleToStringSigFigures(yMin,5));
       yMaxField.setText(Mio.doubleToStringSigFigures(yMax,5));
    }  //  postYMinYMax

// Methods to produce output - main method
	public void drawLabels()
	{
		zeroDerivLabel.setText("f(X0) = " + Mio.doubleToStringSigFigures(zeroDeriv, 5));
		fEvalLabel.setText("f(X1) = " + Mio.doubleToStringSigFigures(fX1, 5));	
		approxEvalLabel.setText("Apr(X1) = " + Mio.doubleToStringSigFigures(approxX1, 5));	
		errorLabel.setText("err = " + Mio.doubleToStringSigFigures(fX1 - approxX1, 5));	
		approx0Label.setText(approxString0);	
		approx1Label.setText(approxString1);	
		approx2Label.setText(approxString2);	
		approx3Label.setText(approxString3);	
		approx4Label.setText(approxString4);	
	}  //drawLabels 

//	Subsidiary output methods
    public void setXBase(double xBase)
    {
    xBaseField.setText(Double.toString(xBase));
    }	//	setXBase
    
    public void setX1(double x1)
    {
    x1Field.setText(Mio.doubleToStringSigFigures(x1,6));
    }	//	setX1
    
}	//	FeedbackPanel
/*
 *	Help are is a simple object to be used for the help panel of the applet.
 *	It is set up as a scrolling region in case the help text is made long
 *	enough to require that, but the default is that no scrollbars are visible.
 */


import java.awt.TextArea;

public class HelpArea extends TextArea
{
	public HelpArea(int width, int height)
	{
//Scrollbar visibility - 0 = both, 1= vert only, 2=horizon only, 3 = none
//		super(String text, int width, int height, int scrollbarVisibility)
		super(" ", width, height, 1);	
		this.setText(
		"This applet was designed to explore how Taylor polynomials approximate\n" +
		"arbitrary functions.  \n\n" +
		
		"The user enters a function, the range of x values that will be graphed, \n" +
		"a base point X0, the degree of the approximating polynomial, and the point \n" +
		"X1 the value is to be approximated at.  The values of X0 and X1 can\n" +
		"either be changed by typing values in text fields or by clicking on the\n" +
		"graph.  If the mouse is dragged rather than clicked the values will\n" +
		"change smoothly.\n\n" +
		
		"The applet graphs the function and the indicated approximating polynomial.\n" +
		"It also gives the value of the function and the approximation at X1 along \n" +
		"the error in the approximation.  A formula for the approximating polynomial\n" +
		"is provided.\n\n" +
		
		"The derivatives for the taylor polynomials are approximated by standard \n" +
		"numeric techniques.  We use balanced difference qutient formulas.\n" +
		"Using d for the del X term we have:\n\n" +
		
		"f'(x) 	 = (f(x+d)-f(x-d))/(2d).\n" +
		"f''(x)	 = (f(x+2d)-2f(x)+f(x-2d))/(4d^2).\n" +
		"f'''(x) = (f(x+3d)-3f(x+d)+3f(x-d)-f(x-3d))/(8d^3).\n" +
		"f''''(x) = (f(x+4d)-4f(x+2d)+6f(x)-4f(x-2d)+f(x-4d))/(16d^4).\n\n" +
		
		"Given the derivatives, the formula for the nth degree taylor polynomial\n" +
		"approximation to f(x) based at X0 is:\n\n" +
		
		"Approx(x) = f(X0) + f'(X0)*(x-X0) + ... + f^(n)(X0)*(x-X0)^n/(n!)\n\n" +
		
		"where f^(n)(X0) is the nth derivative of f evaluated at X0.\n\n\n" +
		
		"The function should be an expression in x.  Implicit multiplication is not allowed.\n" +
		"(Use 3*x rather than 3x.)  Exponentiation is indicated by ^.  \n" +
		"The mathematical constant pi can be in upper or lower case.\n" +  
		"The constant e is indicated by either e or exp(1).\n" + 
		"(These constants only have double precision,  Thus e^x will only appoximate exp(x).\n" + 
		"Trig functions are in radians.  Use the functions toDegrees and toRadians to convert.\n\n" +
		
		"The functions understood by the parser are:\n" +
		"sin,  cos, tan, sec, csc, acos or arccos, asin or arcsin, atan or arctan, \n" +
		"toDegrees, toRadians, abs, ceil, floor, round,\n" +
		"sqrt, exp, log or ln (for natural log), and log10.\n\n" +
		
		"Each of these functions requires one argument enclosed in parentheses.");
		this.setEditable(false);
	}
}	//	HelpArea


/*
 *	AboutArea is an object for the About Panel.  It is a multi-line label
 *	with a fixed text message.
 */


import java.awt.*;

public class AboutArea extends MultiLineLabel
{
	Font theFont = new Font("TimesRoman", Font.BOLD, 18);
	String label = "Taylor Polynomial Approximation Applet\n \n"+
			"Copyright 2001 by Mike May, S.J.\n"+
			"maymk@slu.edu\n \n"+
			"zFunction class inspired by class of that name\n"+
			"from Riemann Sum applet by\n"+
			"Bill Ziemer and Brendon Cheves\n"+
			"Copyright 1996 by California State University," +
			    " Long Beach\n \n"+
			"Function Parsing Copyright 1996 by Darius Bacon";
	int		alignment = 1;		//	center
	int		margin_width =10, margin_height = 10;

	public AboutArea(){
//		super(label, margin_width, margin_height, alignmnet);
//		Possible alignment values are 0 for left, 1 for center and 2 for right.
		super(" ", 10,10, 1);
		this.setLabel(label);
		this.setFont(theFont);
	}
}	//	AboutArea

		
//	MultiLineLabel
// This example is from _Java Examples in a Nutshell_. (http://www.oreilly.com)
// Copyright (c) 1997 by David Flanagan
// This example is provided WITHOUT ANY WARRANTY either expressed or implied.
// You may study, use, modify, and distribute it for non-commercial purposes.
// For any commercial use, see http://www.davidflanagan.com/javaexamples

import java.awt.*;
import java.util.*;

/**
 * A custom component that displays multiple lines of text with specified
 * margins and alignment.  In Java 1.1, we could extend Component instead
 * of Canvas, making this a more efficient "Lightweight component"
 */
public class MultiLineLabel extends Canvas {
  // User-specified attributes
  protected String label;             // The label, not broken into lines
  protected int margin_width;         // Left and right margins
  protected int margin_height;        // Top and bottom margins
  protected int alignment;            // The alignment of the text.
  public static final int LEFT = 0, CENTER = 1, RIGHT = 2; // alignment values
  // Computed state values
  protected int num_lines;            // The number of lines
  protected String[] lines;           // The label, broken into lines
  protected int[] line_widths;        // How wide each line is
  protected int max_width;            // The width of the widest line
  protected int line_height;          // Total height of the font
  protected int line_ascent;          // Font height above baseline
  protected boolean measured = false; // Have the lines been measured?

  // Here are five versions of the constructor
  public MultiLineLabel(String label, int margin_width,
                        int margin_height, int alignment) {
    this.label = label;                 // Remember all the properties
    this.margin_width = margin_width;
    this.margin_height = margin_height;
    this.alignment = alignment;
    newLabel();                         // Break the label up into lines
  }
  public MultiLineLabel(String label, int margin_width, int margin_height) {
    this(label, margin_width, margin_height, LEFT);
  }
  public MultiLineLabel(String label, int alignment) {
    this(label, 10, 10, alignment);
  }
  public MultiLineLabel(String label) { this(label, 10, 10, LEFT); }
  public MultiLineLabel() { this(""); }

  // Methods to set and query the various attributes of the component
  // Note that some query methods are inherited from the superclass.
  public void setLabel(String label) {
    this.label = label;
    newLabel();               // Break the label into lines
    measured = false;         // Note that we need to measure lines
    repaint();                // Request a redraw
  }
  public void setFont(Font f) {
    super.setFont(f);         // tell our superclass about the new font
    measured = false;         // Note that we need to remeasure lines
    repaint();                // Request a redraw
  }
  public void setForeground(Color c) {
    super.setForeground(c);   // tell our superclass about the new color
    repaint();                // Request a redraw (size is unchanged)
  }
  public void setAlignment(int a) { alignment = a; repaint(); }
  public void setMarginWidth(int mw) { margin_width = mw; repaint(); }
  public void setMarginHeight(int mh) { margin_height = mh; repaint(); }
  public String getLabel() { return label; }
  public int getAlignment() { return alignment; }
  public int getMarginWidth() { return margin_width; }
  public int getMarginHeight() { return margin_height; }

  /**
   * This method is called by a layout manager when it wants to
   * know how big we'd like to be.  In Java 1.1, getPreferredSize() is
   * the preferred version of this method.  We use this deprecated version
   * so that this component can interoperate with 1.0 components.
   */
  public Dimension preferredSize() {
    if (!measured) measure();
    return new Dimension(max_width + 2*margin_width,
                         num_lines * line_height + 2*margin_height);
  }

  /**
   * This method is called when the layout manager wants to know
   * the bare minimum amount of space we need to get by.
   * For Java 1.1, we'd use getMinimumSize().
   */
  public Dimension minimumSize() { return preferredSize(); }

  /**
   * This method draws the label (same method that applets use).
   * Note that it handles the margins and the alignment, but that
   * it doesn't have to worry about the color or font--the superclass
   * takes care of setting those in the Graphics object we're passed.
   */
  public void paint(Graphics g) {
    int x, y;
    Dimension size = this.size();  // use getSize() in Java 1.1
    if (!measured) measure();
    y = line_ascent + (size.height - num_lines * line_height)/2;
    for(int i = 0; i < num_lines; i++, y += line_height) {
      switch(alignment) {
      default:
      case LEFT:    x = margin_width; break;
      case CENTER:  x = (size.width - line_widths[i])/2; break;
      case RIGHT:   x = size.width - margin_width - line_widths[i]; break;
      }
      g.drawString(lines[i], x, y);
    }
  }

  /** This internal method breaks a specified label up into an array of lines.
   *  It uses the StringTokenizer utility class. */
  protected synchronized void newLabel() {
    StringTokenizer t = new StringTokenizer(label, "\n");
    num_lines = t.countTokens();
    lines = new String[num_lines];
    line_widths = new int[num_lines];
    for(int i = 0; i < num_lines; i++) lines[i] = t.nextToken();
  }

  /** This internal method figures out how the font is, and how wide each
   *  line of the label is, and how wide the widest line is. */
  protected synchronized void measure() {
    FontMetrics fm = this.getToolkit().getFontMetrics(this.getFont());
    line_height = fm.getHeight();
    line_ascent = fm.getAscent();
    max_width = 0;
    for(int i = 0; i < num_lines; i++) {
      line_widths[i] = fm.stringWidth(lines[i]);
      if (line_widths[i] > max_width) max_width = line_widths[i];
    }
    measured = true;
  }
}	//	MultiLineLabel

// ZFunction Class
/*  Modified by Mike May,  August 2001
 *	Inspired by a class witten Bill Ziemer and Brendon Cheves
 *	that was based upon the Java Polygon class
 *	
 *public class ZFunction
 *	public ZFunction()
 *	
 *	public void parse( String f )
 *	
 *	the parse method sends the string over to the Parser class for parsing
 *	
 *
 *	public double value(double x)
 *	
 *	This method evaluates the function at x.
 *	
 *
 *	public void findNumDerivs()
 *
 *	Attached to Taylor polynomials is the need to find derivatives at a base
 *	point.  This method does that numerically.
 *
 *	
 *	public void determineYMinYMax()
 *	public void setXRange(double f, double l)
 *
 *	The last two methods are useful in setting finding appropriate limits
 *	to use when graphing the function.
 *
 */


// java packages
import java.applet.*;
import java.awt.*;
import java.lang.Math;

import expr.*;	// expr package Copyright 1996 by Darius Bacon

public class ZFunction
{
	//Real number bounds for the function
	public double yMin = -5.0, yMax = 5.0;
	public double xMin = -5.0, xMax = 5.0;
	public double xBase = 1.5, xDel = 0.01, zeroDeriv, firstDeriv, 
					secondDeriv, thirdDeriv, fourthDeriv;
	//	created for access to infinity constants
	Double doubleCheck = new Double(1.0);
	
 	public Expr expr; 
	public Variable xvar;

	public ZFunction()
	{
		// set up the parsing variables
		xvar = Variable.make("x");
		Variable.make("pi").set_value (Math.PI);	
		Variable.make("PI").set_value (Math.PI);	
		Variable.make("Pi").set_value (Math.PI);	
		Variable.make("e").set_value (Math.exp(1));	
/*
 * 	¼ and e are not in the parsing constructor
 */
	}  //  ZFunction constructor

	
	/*
	 *	Parse the function
	 */	 
	public void parse( String f )
 	{
	   	try
	   	{expr = Parser.parse(f);}
		catch (Syntax_error se)
		{
			System.err.println ("Syntax error: " + se);
			return;
		}
	}  // parse
	
	public double value(double x)
	{
		xvar.set_value(x);
		return expr.value();
	}  //  value
			
	public void findNumDerivs()
	{
		zeroDeriv = value(xBase);
		firstDeriv = (value(xBase + xDel) - value(xBase - xDel))/(2.0*xDel);
		secondDeriv = (value(xBase + 2*xDel) - 2*value(xBase) + value(xBase - 2*xDel))
				/ (4.0*xDel*xDel);
		thirdDeriv = (value(xBase + 3*xDel) - 3*value(xBase + xDel) 
				+ 3*value(xBase - xDel) - value(xBase - 3*xDel))/(8.0*xDel*xDel*xDel);
		fourthDeriv = (value(xBase + 4*xDel) - 4*value(xBase + 2*xDel) +6*value(xBase)
				- 4*value(xBase - 2*xDel) + value(xBase - 4*xDel))/(16.0*xDel*xDel*xDel*xDel);
	}	//	findNumDerivs
		
	public void determineYMinYMax()
	{
		double dx = (xMax-xMin)/500.0, tempY;		
		yMin = doubleCheck.POSITIVE_INFINITY;
		yMax = doubleCheck.NEGATIVE_INFINITY;
		
		for(int i=0;i<=500;i++)
		{
			tempY = value(xMin + i*dx);
			if(tempY > yMax) 		yMax = tempY;			
			if(tempY < yMin)	yMin = tempY;
		}
		//	Round to three decimal places
		yMax = ((int)(yMax*1000))/1000.0;
		yMin = ((int)(yMin*1000))/1000.0;
		//	Fudge things so that the X-axis is included
		if (yMin > -.5) yMin = -.5;
		if (yMax < 0.5) yMax = 0.5;
	}  //  determineYminYMax

	 
	// set x range manually
	public void setXRange(double f, double l)
	{
		xMin = f;
		xMax = l;
	}  //  setXRange
	
} // end of ZFunction Class




////
////
//		Code for the parser
////
////

		
// Mathematical expressions.
// Copyright 1996 by Darius Bacon; see the file COPYING.

// 14May96: added constant folding

// 07JUL1999 - version 1.0 - C. Pheatt
// 05AUG2000 - Mike May, S.J.
//				add in SEC, CSC. ToDEG, TORAD
package expr;

/**
 * A mathematical expression, built out of literal numbers, variables,
 * arithmetic operators, and elementary functions.  The operator names
 * are from java.lang.Math.
 */
public abstract class Expr {

  /** @return the value given the current variable values */
  public abstract double value ();

  /** Binary operator. */  public static final int ADD = 0;  
  /** Binary operator. */  public static final int SUB = 1;
  /** Binary operator. */  public static final int MUL = 2;
  /** Binary operator. */  public static final int DIV = 3;
  /** Binary operator. */  public static final int POW = 4;
  /** Binary operator. */  public static final int MOD = 5;
  /** Binary operator. */  public static final int LTH = 6;
  /** Binary operator. */  public static final int LEQ = 7;
  /** Binary operator. */  public static final int GTH = 8;
  /** Binary operator. */  public static final int GEQ = 9;
  /** Binary operator. */  public static final int EQU = 10;
  /** Binary operator. */  public static final int NEQ = 11;
  /** Binary operator. */  public static final int AND = 12;
  /** Binary operator. */  public static final int ORR = 13;

  /** Unary operator. */        public static final int ABS   = 100;
  /** Unary operator. */        public static final int ACOS  = 101;
  /** Unary operator. */        public static final int ASIN  = 102;
  /** Unary operator. */        public static final int ATAN  = 103;
  /** Unary operator. */        public static final int CEIL  = 104;
  /** Unary operator. */        public static final int COS   = 105;
  /** Unary operator. */        public static final int EXP   = 106;
  /** Unary operator. */        public static final int FLOOR = 107;
  /** Unary operator. */        public static final int LOG   = 108;
  /** Unary minus operator. */  public static final int NEG   = 109;
  /** Unary operator. */        public static final int ROUND = 110;
  /** Unary operator. */        public static final int SIN   = 111;
  /** Unary operator. */        public static final int SQRT  = 112;
  /** Unary operator. */        public static final int TAN   = 113;
  /** Unary operator. */        public static final int LOG10 = 114;
  /** Unary operator. */        public static final int SEC   = 115;
  /** Unary operator. */        public static final int CSC   = 116;
  /** Unary operator. */        public static final int TODEG = 117;
  /** Unary operator. */        public static final int TORAD = 118;
  /** Unary operator. */       // public static final int FACT  = 115;
  
  public static Expr make_literal (double v) { 
    return new Literal (v); 
  }
  public static Expr make_var_ref (Variable var) {
    return new Var_ref (var);
  }
  /** 
   * @param rator unary operator
   * @param rand operand
   */
  public static Expr make_app1 (int rator, Expr rand) {
    Expr app = new App1 (rator, rand);
    return rand instanceof Literal ? new Literal (app.value ()) : app;
  }
  /** 
   * @param rator binary operator
   * @param rand0 left operand
   * @param rand1 right operand
   */
  public static Expr make_app2 (int rator, Expr rand0, Expr rand1) {
    Expr app = new App2 (rator, rand0, rand1);
    return rand0 instanceof Literal && rand1 instanceof Literal
	     ? new Literal (app.value ()) 
	     : app;
  }
}	//	Expr

// These classes are all private to this module so that I can get rid
// of them later.  For applets you want to use as few classes as
// possible to avoid http connections at load time; it'd be profitable
// to replace all these subtypes with bytecodes for a stack machine,
// or perhaps a type that's the union of all of them (see class Node
// in java/demo/SpreadSheet/SpreadSheet.java).

class Literal extends Expr {
  double v;
  Literal (double _v) { v = _v; }
  public double value () { return v; }
}	//	Literal

class Var_ref extends Expr {
  Variable var;
  Var_ref (Variable _var) { var = _var; }
  public double value () { return var.value (); }
}	//	Var_ref

class App1 extends Expr {
  int rator;
  Expr rand;

  App1 (int _rator, Expr _rand) { rator = _rator; rand = _rand;  }

  public double value () {
    double arg = rand.value ();
    switch (rator) {
    case ABS:   return Math.abs (arg);
    case ACOS:  return Math.acos (arg);
    case ASIN:  return Math.asin (arg);
    case ATAN:  return Math.atan (arg);
    case CEIL:  return Math.ceil (arg);
    case COS:   return Math.cos (arg);
    case EXP:   return Math.exp (arg);
//    case FACT:  return MiscFunctions.fact (arg);
    case FLOOR: return Math.floor (arg);
    case LOG:   return Math.log (arg);
    case LOG10: return Math.log (arg) * 0.434294481903251827651128918916;
    case NEG:   return -arg;
    case ROUND: return Math.round (arg);
    case SIN:   return Math.sin (arg);
    case SQRT:  return Math.sqrt (arg);
    case TAN:   return Math.tan (arg);
    case SEC:   return 1/Math.cos (arg);
    case CSC:   return 1/Math.sin (arg);
    case TODEG: return 180*(arg)/(3.141582653589793);
    case TORAD: return (Math.PI)*(arg)/180;

    default: throw new RuntimeException ("BUG: bad rator");
    }
  }
}	//	App1

class App2 extends Expr {
  int rator;
  Expr rand0, rand1;

  App2 (int _rator, Expr _rand0, Expr _rand1) { 
    rator = _rator; rand0 = _rand0; rand1 = _rand1;  }
  public double value () {
    double arg0 = rand0.value ();
    double arg1 = rand1.value ();
    switch (rator) {
    case ADD:  return arg0 + arg1;
    case SUB:  return arg0 - arg1;
    case MUL:  return arg0 * arg1;
    case DIV:  return arg0 / arg1;   // check for division by 0?
    case POW:  return Math.pow (arg0, arg1);
    case MOD:  return arg0 % arg1;
    case LTH:  if(arg0 < arg1) return 1; else return 0;
    case LEQ:  if(arg0 <= arg1) return 1; else return 0;
    case GTH:  if(arg0 > arg1) return 1; else return 0;
    case GEQ:  if(arg0 >= arg1) return 1; else return 0;
    case EQU:  if(arg0 == arg1) return 1; else return 0;
    case NEQ:  if(arg0 != arg1) return 1; else return 0;
    case AND:  if((arg0 != 0) && (arg1 != 0)) return 1; else return 0;
    case ORR:  if((arg0 != 0) || (arg1 != 0)) return 1; else return 0;

    default: throw new RuntimeException ("BUG: bad rator");
    }
  }
}	//	App2

//	End package Expr


// Operator-precedence parser.
// Copyright 1996 by Darius Bacon; see the file COPYING.

// 14May96: bugfix. 
//	StreamTokenizer treated '-<number>' as a numeric token, not a minus
//	operator followed by a number.  Fix: make '-' an ordinaryChar.

// 12May97: Changed the precedence of unary minus to be lower than 
//      multiplication, so -y^2 is like -(y^2), not (-y)^2.

// 07JUL1999 - version 1.0 - C. Pheatt
// 05AUG2000 - Mike May, S.J.
//				add in sec, csc, toDegrees, toRadians, ln

package expr;
import java.io.*;

/** 
  Parses strings representing mathematical formulas with variables.
  The following operators, in descending order of precedence, are
  defined:

  <UL>
  <LI>^ (raise to a power)
  <LI>* /
  <LI>Unary minus (-x)
  <LI>+ -
  </UL>

  ^ associates right-to-left; other operators associate left-to-right.

  <P>These functions are defined: 
    abs, acos, asin, atan, 
    ceil, cos, exp, floor, 
    log, round, sin, sqrt, 
    tan.  Each requires one argument enclosed in parentheses.

  <P>Whitespace outside identifiers is ignored.

  <P>The syntax-error messages aren't very informative, unfortunately.
  IWBNI it indicated where in the input string the parse failed, but 
  that'd be kind of a pain since our scanner is a StreamTokenizer.  A
  hook for that info should've been built into StreamTokenizer.

  <P>Examples:
  <UL>
  <LI>42
  <LI>2-3
  <LI>cos(x^2) + sin(x^2)
  <UL>
 */
public class Parser {
  static StreamTokenizer tokens;

  public static Expr parse (String input) throws Syntax_error {
    tokens = new StreamTokenizer (new StringReader (input));
    tokens.ordinaryChar ('/');
    tokens.ordinaryChar ('-');
    tokens.ordinaryChar ('%');
    tokens.ordinaryChar ('>');
    tokens.ordinaryChar ('<');
    tokens.ordinaryChar ('=');
    tokens.ordinaryChar ('!');
    tokens.ordinaryChar ('&');
    tokens.ordinaryChar ('|');
    next ();
    Expr expr = parse_expr (0);
    if (tokens.ttype != StreamTokenizer.TT_EOF)
      throw new Syntax_error ("Incomplete expression: " + input);
    return expr;
  }

  static void next () {
    try { tokens.nextToken (); }
    catch (IOException e) { throw new RuntimeException ("I/O error: " + e); }
  }

  static void push () {
     tokens.pushBack(); 
  }

  static void expect (int ttype) throws Syntax_error {
    if (tokens.ttype != ttype)
      throw new Syntax_error ("'" + (char) ttype + "' expected");
    next ();
  }

  static Expr parse_expr (int precedence) throws Syntax_error {
    Expr expr = parse_factor ();
  loop: for (;;) {
      int l, r, rator;   

      // The operator precedence table.
      // l = left precedence, r = right precedence, rator = operator.
      // Higher precedence values mean tighter binding of arguments.
      // To associate left-to-right, let r = l+1;
      // to associate right-to-left, let r = l.

      switch (tokens.ttype) {

      case '|': 
        l = 2; r = 3; rator = Expr.ORR;
        next();
        if(tokens.ttype != '|') push();
        break;

      case '&': 
        l = 4; r = 5; rator = Expr.AND; 
        next();
        if(tokens.ttype != '&') push();
        break;

      case '=': 
        l = 6; r = 7; rator = Expr.EQU; 
        next();
        if(tokens.ttype != '=') push();
        break;
        
      case '!': 
        l = 6; r = 7; rator = Expr.NEQ; 
        next();
        if(tokens.ttype != '=') push();
        break;
      
      case '<':
        l = 8; r = 9;
        next();
        if(tokens.ttype != '=')  {
            push();
            rator = Expr.LTH; 
        }
        else {
            rator = Expr.LEQ; 
        }    
        break;
        
      case '>':
        l = 8; r = 9; 
        next();
        if(tokens.ttype != '=')  {
            push();
            rator = Expr.GTH; 
        }
        else {
            rator = Expr.GEQ; 
        }    
        break;
      
      case '+': l = 10; r = 11; rator = Expr.ADD; break;
      case '-': l = 10; r = 11; rator = Expr.SUB; break;
	
      case '%': l = 20; r = 21; rator = Expr.MOD; break;
      case '*': l = 20; r = 21; rator = Expr.MUL; break;
      case '/': l = 20; r = 21; rator = Expr.DIV; break;
	
      case '^': l = 30; r = 30; rator = Expr.POW; break; 
	
      default: break loop;
      }

      if (l < precedence)
	break loop;

      next ();
      expr = Expr.make_app2 (rator, expr, parse_expr (r));
    }
    return expr;
  }

  static String[] procs = {
    "abs", "acos","arccos", "asin",
    "arcsin", "atan", "arctan",
    "ceil", "cos", "exp", "floor", 
    "log", "ln", "round", "sin", "sqrt", 
    "tan", "log10", "sec", "csc",
    "toDegrees", "toRadians"//, "fact"
  };
  static int[] rators = {
    Expr.ABS, Expr.ACOS, Expr.ACOS, Expr.ASIN,
    Expr.ASIN, Expr.ATAN, Expr.ATAN, 
    Expr.CEIL, Expr.COS, Expr.EXP, Expr.FLOOR,
    Expr.LOG, Expr.LOG, Expr.ROUND, Expr.SIN, Expr.SQRT, 
    Expr.TAN , Expr.LOG10, Expr.SEC, Expr.CSC,
    Expr.TODEG, Expr.TORAD//, Expr.FACT
  };
	
  static Expr parse_factor () throws Syntax_error {
    switch (tokens.ttype) {
    case StreamTokenizer.TT_NUMBER: {
      Expr lit = Expr.make_literal (tokens.nval);
      next ();
      return lit;
    }
    case StreamTokenizer.TT_WORD: {
      for (int i = 0; i < procs.length; ++i)
	if (procs [i].equals (tokens.sval)) {
	  next ();
	  expect ('(');
	  Expr rand = parse_expr (0);
	  expect (')');
	  return Expr.make_app1 (rators [i], rand);
	}

      Expr var = Expr.make_var_ref (Variable.make (tokens.sval));
      next ();
      return var;
    }
    case '(': {
      next ();
      Expr enclosed = parse_expr (0);
      expect (')');
      return enclosed;
    }
    case '-': 
      next ();
      return Expr.make_app1 (Expr.NEG, parse_expr (15));
    default:
      throw new Syntax_error ("Expected a factor");
    }
  }
}

// Syntax-error exception.
// Copyright 1996 by Darius Bacon; see the file COPYING.

package expr;

public class Syntax_error extends Exception {
  public Syntax_error (String complaint) { super (complaint); }
}

// Put the expression evaluator through its paces.

// Sample usage:

// $ java expr.Test '3.14159 * x^2' 0 4 1
// 0
// 3.14159
// 12.5664
// 28.2743
// 50.2654
//
// $ java expr.Test 'sin (pi/4 * x)' 0 4 1
// 0
// 0.707107
// 1
// 0.707107
// 1.22461e-16

//package expr;

import expr.*;

public class Test {
  public static void main (String[] args) {

    Expr expr;
   	try
   	{
   		expr = Parser.parse (args [0]);
   	}
	catch (Syntax_error e)
	{
		System.err.println ("Syntax error: " + e);
		return;
	}

    double low  = Double.valueOf (args [1]).doubleValue ();
    double high = Double.valueOf (args [2]).doubleValue ();
    double step = Double.valueOf (args [3]).doubleValue ();
      
    Variable x = Variable.make ("x");
    Variable.make ("pi").set_value (Math.PI);

    for (double xval = low; xval <= high; xval += step) {
      x.set_value (xval);
      System.out.println (expr.value ());
    }
  }
}

/*
	Trivial library - 4/96 PNL
*/

public class TrivialClass {
	
	public TrivialClass() {
	}
}

// Variables associate values with names.
// Copyright 1996 by Darius Bacon; see the file COPYING.

package expr;

import java.util.Hashtable;

/**
 * Variables associate values with names.
 */
public class Variable {
  static Hashtable variables = new Hashtable ();

  /**
   * Return <EM>the</EM> variable named `_name'.  
   * make (s1) == make (s2) iff s1.equals (s2).
   */
  static public Variable make (String _name) {
    Variable result = (Variable) variables.get (_name);
    if (result == null)
      variables.put (_name, result = new Variable (_name));
    return result;
  }

  String name;
  double val;

  public Variable (String _name) { name = _name; val = 5; }

  public String toString () { return name; }
  public double value () { return val; }
  public void set_value (double _val) { val = _val; }
}

/*  Copying file for expr package
Copyright 1996 by Darius Bacon.

Permission is granted to anyone to use this software for any
purpose on any computer system, and to redistribute it freely,
subject to the following restrictions:

1. The author is not responsible for the consequences of use of
   this software, no matter how awful, even if they arise from 
   defects in it.

2. The origin of this software must not be misrepresented, either
   by explicit claim or by omission.

3. Altered versions must be plainly marked as such, and must not
   be misrepresented as being the original software.
   */
   
//	MIO = My Input Output
//	It is a collection of methods for cleaning up input and output.
//	Mike May, S.J.
//	maymk@slu.edu
//	August, 2001

/*	Current method list
 *	public static int intFromTextField(TextField tf) 
 *	public static double doubleFromTextField(TextField tf) 
 *	public static String doubleToStringDecPrecision(double d, int decfigs) 
 *	public static String doubleToStringNoE(TextField tf) 
 *	public static String doubleToStringSigFigures(double d, int sigfigs)
 *
 *	This version tries to interpret doubles first in Java format, then as
 *	an NFunction.  Java format allows sx.xxxxxxEsxxx while NFunctions
 *	allow usual mathematical constructions.
 */
package MIO;
import	java.awt.*;
import	ZFunction;

public  class Mio {	

	public Mio() {}

/** This is a utility routine to retrieve an integer from a text field. */
	public static int intFromTextField(TextField tf) 
	{
		String  string;
		int     value;
		
		string = tf.getText();
		try {
		   value = Integer.parseInt(string);
		} catch (Exception e) {
       System.out.println("Warning, noninteger read for integer value.");
       System.out.println("The variable has be set to 0.");
		   value = 0;
		}		   
		return value;
	}	//	intFromTextField

	public static double doubleFromTextField(TextField tf) 
	{
		ZFunction	func = new ZFunction();   		
		String		string;
		double		value;
		
		string = tf.getText();
		try {
			value = Double.valueOf(string).doubleValue();
		} catch (Exception e) {
		try {
		func.parse(string);
			value = func.value(1);
		} catch (Exception se) {
       System.out.println("Warning, nondouble read for double value.");
       System.out.println("The variable has be set to 0.0.");
		   value = 0.0;
		}}
		return value;
	}	//	doubleFromTextField


	//	This procedure is intended to turn a double into a a string with a 
	// specified decimal precision.
	public static String doubleToStringDecPrecision(double d, int decfigs) 
	{
	     if (d == 0) return "0.0";
	     else return String.valueOf((Math.round(d*(Math.pow(10,decfigs))))/ 
                 (Math.pow(10,decfigs)));
	}	//	doubleToStringDecPrecision


	//	This procedure is intended to turn a double into a string with the added
	//	provision being that the string not use E notation.
	//	The point is if the string is then to be used in a an expression by a parser.
	//	The output format is either (sxxx.xxx) or (sx.xxxx)*10^(sxxx).
	public static String doubleToStringNoE(double d) 
	{
		String s1 = Double.toString(d);
		int loc = s1.indexOf("E");
		int len = s1.length();
			System.out.println("E at " + loc + " length is " + len);
		if (loc == -1) return s1;
		else return "(" + s1.substring(0,loc) +")*10^(" + s1.substring(loc+1,len) + ")";
	}	//	doubleToStringNoE


	//	This procedure is intended to turn a double into a string with a specified
	//	number of significant figures.  We need to divide into cases on whether
	//	s1 is already in scientific notation.
	public static String doubleToStringSigFigures(double d, int sigfigs)
	{
		String s1 = Double.toString(d);
		int len = s1.length();
		int loc = s1.indexOf("E");
		int posdot = s1.indexOf(".");
		int pos;
		if (sigfigs < 1) return s1;
		if (loc != -1)	// There is an E in s1
		{
			pos = Math.min(posdot+sigfigs, loc);	
			return  s1.substring(0,pos) +"E" + s1.substring(loc+1,len);
		}
		else	// no E in the string representation s1
		{
			pos = sigfigs + 1;
			if (s1.indexOf("-") == 0) pos += 1;
			if (posdot <= pos)	//	Need to keep figures right of decimal
			{	
				int dotMove = (Double.toString(d*1000.0).indexOf(".") - posdot);
				int leadZeroes = 3 - dotMove;	// count leading zeroes and adjust
				if (leadZeroes > 0) pos += leadZeroes;
				pos = Math.min(pos, len);
				return s1.substring(0, pos);
			}
			int numZeroes = posdot - pos;
			String s2 = s1.substring(0, pos);
			for (int i = 0; i< numZeroes; i++) s2 = s2 + "0";
			return s2 + ".0";
		}
	}	//	doubleToStringSigFigures


}	//	class MIO

