// Main class // /* * Sequences and Series Applet * By Mike May, S.J., August 2001 * e-mail: maymk@slu.edu * * expr package Copyright 1996 by Darius Bacon */ // Last update 8/2/2001 /* * * The applet graphs a collection of sequences and their related series. * It is designed to facilitate visual exploration of the subject. * * The structure of the program has a main class SeqSeries. * The work is broken into a number of visible classes: * DrawingPanel, FeedbackPanel, HelpArea, and AboutArea. * The names of these classes are fairly self descriptive. * This NFunction class is used to turn mathematical strings into * values. It uses a parser that calls on the following classes: * Expr, Parser, Syntax_error, Test, TrivialClass and Variable. * The subsidaiary classes of FeedbackPanel are InputPanel, ControlPanel, * OutputPanel, and DomainPanel. * MultilineLabel is used by AboutArea. * MIO is a collection of input and output routines used. */ /* * * public class SeqSeries extends Applet * public void init() * public boolean action(Event e, Object arg) * * 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 importFeedbackPanelValues() * * The methods of this class involve when the DrawingPanel and FeedbackPanel * need to communicate with each other or when an action on one calls for a response * on the other. The majority of the work is done with the fixMain method. * A subsidiary method of fixMain is importFeedbackPanelValues. */ // java packages import java.applet.*; import java.awt.*; import java.lang.Math; import expr.*; // f(x) parser by Darius Bacon // Modified by Mike May, S.J. public class SeqSeries extends Applet { // Declare variables int drawingPanelWidth = 470, drawingPanelHeight = 405; // int clickIndex = 0; // GUI elements Button helpButton, aboutButton, mainButton; Label titleLabel; CardLayout theCards; Panel mainCd, mainPanel, cardChoicePanel; HelpArea helpText; AboutArea aboutText; FeedbackPanel feedbackPanel; DrawingPanel theDrawingPanel; public void init() { this.setLayout(null); resize(720,500); // Set up the pieces titleLabel = new Label("Sequences and Series", 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.setBounds(480,0,200,405); mainCd.add(feedbackPanel, BorderLayout.EAST); feedbackPanel.setLayout(null); // create the drawing panel and add it to the main cd theDrawingPanel = new DrawingPanel(this); mainCd.add(theDrawingPanel, BorderLayout.CENTER); // move the data from feedback panel to drawing panel and initialize feedbackPanel.collectData(); importFeedbackPanelValues(); 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.setBounds(200,460,300,20); cardChoicePanel.setLayout(new GridLayout(1,0,10,0)); mainButton = new Button("Main Panel"); cardChoicePanel.add(mainButton); 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 == mainButton) { theCards.show(mainPanel,"main"); feedbackPanel.collectData(); fixMain(); paint(theDrawingPanel.getGraphics()); return true; } else return super.action(e,arg); } // action public void fixMain() { importFeedbackPanelValues(); theDrawingPanel.updateDrawingValues(); paint(getGraphics()); theDrawingPanel.repaint(); } // fixMain public void importFeedbackPanelValues() { theDrawingPanel.termsState = feedbackPanel.termsState; theDrawingPanel.sumsState = feedbackPanel.sumsState; theDrawingPanel.ratioState = feedbackPanel.ratioState; theDrawingPanel.pointsState = feedbackPanel.pointsState; theDrawingPanel.linesState = feedbackPanel.linesState; theDrawingPanel.aNState = feedbackPanel.aNState; theDrawingPanel.bNState = feedbackPanel.bNState; theDrawingPanel.geomState = feedbackPanel.geomState; theDrawingPanel.harmState = feedbackPanel.harmState; theDrawingPanel.expState = feedbackPanel.expState; theDrawingPanel.nStart = feedbackPanel.nStart; theDrawingPanel.nGraph = feedbackPanel.nGraph; theDrawingPanel.nStep = feedbackPanel.nStep; theDrawingPanel.toViewValue = feedbackPanel.toViewValue; theDrawingPanel.aNString = feedbackPanel.aNString; theDrawingPanel.bNString = feedbackPanel.bNString; theDrawingPanel.geomString = feedbackPanel.geomString; theDrawingPanel.harmString = feedbackPanel.harmString; theDrawingPanel.expString = feedbackPanel.expString; theDrawingPanel.yMin = feedbackPanel.yMin; theDrawingPanel.yMax = feedbackPanel.yMax; } // importfeedbackPanelValues } // SeqSeries class //// //// //// // Other pieces //// //// /* * The drawing panel is an object for the graphical display of the applet. * *public class DrawingPanel extends Canvas * public DrawingPanel(SeqSeries seqSeries) * * The constructor does little more than extend canvas and identify the applet * and applet so that information can be passed back and forth. * * * public void updateDrawingValues() * public void repaint() * public void paint(Graphics g) * public void plotSums(Graphics g, String funcString, Color color) * public void plotRatios(Graphics g, String funcString, Color color) * public void plotTerms(Graphics g, String funcString, Color color) * public void drawAxis(Graphics g) * public void drawLimits(Graphics g) * * The repaint method simply gets the graphics and calls paint which does all the work. * The other methods in this group do parts of the plotting. * * * 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 { // Declare variables // variables concerned with the size of the panel and how much is used // for the actual drawing. // The left offset and the vertical offsets are just to // make everything look nice. Nothing important is graphed in the offset // area. The panel is set up with a single offset // at the top and a double one at the bottom of the panel. int leftXOffset = 40,drawingPanelWidth = 470, drawingPanelHeight = 405, YOFFSET = 20, pointWidth, sectionCount; // variables imported or exported to the feedback panel boolean termsState, sumsState, ratioState, pointsState = true, linesState = false; boolean aNState, bNState, geomState, harmState, expState; int nStart = 1, nGraph = 1, nStep, toViewValue; String aNString, bNString, geomString, harmString, expString; double yMin = -5.0, yMax = 5.0; NFunction func; SeqSeries applet; public DrawingPanel(SeqSeries seqSeries) { super(); func = new NFunction(); pointsState = true; linesState = false; applet = seqSeries; } // DrawingPanel constructor public void updateDrawingValues() { drawingPanelWidth = getSize().width; drawingPanelHeight = getSize().height; pointWidth = (drawingPanelWidth - 2*leftXOffset)/4; sectionCount = pointWidth/20; } // updateDrawingValues public void repaint() { paint(getGraphics()); } // repaint public void paint(Graphics g) { updateDrawingValues(); // clear the canvas g.clearRect(0,0,drawingPanelWidth,drawingPanelHeight); // add axes and limits drawAxis(g); drawLimits(g); // plot terms if(termsState == true) { if(aNState == true) plotTerms(g, aNString, Color.red); if(bNState == true) plotTerms(g, bNString, Color.blue); if(geomState == true) plotTerms(g, geomString, Color.green); if(harmState == true) plotTerms(g, harmString, Color.magenta); if(expState == true) plotTerms(g, expString, Color.cyan); } // plot sums if(sumsState == true) { if(aNState == true) plotSums(g, aNString, Color.red); if(bNState == true) plotSums(g, bNString, Color.blue); if(geomState == true) plotSums(g, geomString, Color.green); if(harmState == true) plotSums(g, harmString, Color.magenta); if(expState == true) plotSums(g, expString, Color.cyan); } // plot ratios if(ratioState == true) { if(aNState == true) plotRatios(g, aNString, Color.red); if(bNState == true) plotRatios(g, bNString, Color.blue); if(geomState == true) plotRatios(g, geomString, Color.green); if(harmState == true) plotRatios(g, harmString, Color.magenta); if(expState == true) plotRatios(g, expString, Color.cyan); } } // paint public void plotSums(Graphics g, String funcString, Color color) { int xPixel1, xPixel2, yPixel1, yPixel2, nCount; double sum = 0.0, temp; func.parse(funcString); g.setColor(color); for(int i = nStart; i <= nGraph; i++) { temp = func.value(i); if(!(temp <= Double.POSITIVE_INFINITY)) temp = 0; sum += temp; } nCount = nGraph; xPixel1 = leftXOffset; yPixel1 = yRealToPixel(sum); if((yPixel1 >= 0)&&(yPixel1 <= drawingPanelHeight)) { g.drawLine(xPixel1, yPixel1 - 2, xPixel1, yPixel1 + 2); g.drawLine(xPixel1 - 2, yPixel1, xPixel1 + 2, yPixel1); } for(int j = 1; j <= pointWidth; j++) { nCount = nGraph + (j-1)*nStep; xPixel2 = leftXOffset + 4*j; for(int i = nCount + 1; i <= (nCount + nStep); i++) { temp = func.value(i); if(!(temp <= Double.POSITIVE_INFINITY)) temp = 0; sum += temp; } yPixel2 = yRealToPixel(sum); if((yPixel2 >= 0)&&(yPixel2 <= drawingPanelHeight)) { g.drawLine(xPixel2, yPixel2 - 2, xPixel2, yPixel2 + 2); g.drawLine(xPixel2 - 2, yPixel2, xPixel2 + 2, yPixel2); } if(pointsState == false) { if( ((0 <= yPixel2) && (yPixel2 <= drawingPanelHeight)) || ((0 <= yPixel1) && (yPixel1 <= drawingPanelHeight)) ) g.drawLine(xPixel1, yPixel1, xPixel2, yPixel2); } xPixel1 = xPixel2; yPixel1 = yPixel2; } } // plotSums public void plotRatios(Graphics g, String funcString, Color color) { int xPixel1, xPixel2, yPixel1, yPixel2, nCount; double temp, ytemp1, ytemp2; func.parse(funcString); g.setColor(color); nCount = nGraph; xPixel1 = leftXOffset; ytemp1 = func.value(nCount); ytemp2 = func.value(nCount + 1); if(!(ytemp1 <= Double.POSITIVE_INFINITY))temp = 0; else temp = ytemp2/ytemp1; yPixel1 = yRealToPixel(temp); if((yPixel1 >= 0)&&(yPixel1 <= drawingPanelHeight)) { g.drawLine(xPixel1, yPixel1 - 2, xPixel1, yPixel1 + 2); g.drawLine(xPixel1 - 2, yPixel1, xPixel1 + 2, yPixel1); } for(int i = 1; i <= pointWidth; i++) { nCount = nGraph + i*nStep; xPixel2 = leftXOffset + 4*i; ytemp1 = func.value(nCount); ytemp2 = func.value(nCount + 1); if(!(ytemp1 <= Double.POSITIVE_INFINITY))temp = 0; else temp = ytemp2/ytemp1; yPixel2 = yRealToPixel(temp); if((yPixel2 >= 0)&&(yPixel2 <= drawingPanelHeight)) { g.drawLine(xPixel2, yPixel2 - 2, xPixel2, yPixel2 + 2); g.drawLine(xPixel2 - 2, yPixel2, xPixel2 + 2, yPixel2); } if(pointsState == false) { if( ((0 <= yPixel2) && (yPixel2 <= drawingPanelHeight)) || ((0 <= yPixel1) && (yPixel1 <= drawingPanelHeight)) ) g.drawLine(xPixel1, yPixel1, xPixel2, yPixel2); } xPixel1 = xPixel2; yPixel1 = yPixel2; } } // plotRatios public void plotTerms(Graphics g, String funcString, Color color) { int xPixel1, xPixel2, yPixel1, yPixel2, nCount; double temp; func.parse(funcString); g.setColor(color); nCount = nGraph; xPixel1 = leftXOffset; yPixel1 = yRealToPixel(func.value(nCount)); if((yPixel1 >= 0)&&(yPixel1 <= drawingPanelHeight)) { g.drawLine(xPixel1, yPixel1 - 2, xPixel1, yPixel1 + 2); g.drawLine(xPixel1 - 2, yPixel1, xPixel1 + 2, yPixel1); } for(int i = 1; i <= pointWidth; i++) { nCount = nGraph + i*nStep; xPixel2 = leftXOffset + 4*i; temp = func.value(nCount); if(!(temp <= Double.POSITIVE_INFINITY)) temp = 0; yPixel2 = yRealToPixel(temp); if((yPixel2 >= 0)&&(yPixel2 <= drawingPanelHeight)) { g.drawLine(xPixel2, yPixel2 - 2, xPixel2, yPixel2 + 2); g.drawLine(xPixel2 - 2, yPixel2, xPixel2 + 2, yPixel2); } if(pointsState == false) { if( ((0 <= yPixel2) && (yPixel2 <= drawingPanelHeight)) || ((0 <= yPixel1) && (yPixel1 <= drawingPanelHeight)) ) g.drawLine(xPixel1, yPixel1, xPixel2, yPixel2); } xPixel1 = xPixel2; yPixel1 = yPixel2; } } // plotTerms // draw the axis public void drawAxis(Graphics g) { int xAxisPixel, yAxisPixel, yPixel; 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(20,xAxisPixel,drawingPanelWidth - 20,xAxisPixel); if (ratioState == true) { int onePixel = yRealToPixel(1.0); g.drawLine(0,onePixel,drawingPanelWidth - 20,onePixel); g.setColor(Color.red); g.drawString("1",drawingPanelWidth - 15,onePixel + 4); g.setColor(Color.black); onePixel = yRealToPixel(-1.0); g.drawLine(0,onePixel,drawingPanelWidth - 20,onePixel); g.setColor(Color.red); g.drawString("-1",drawingPanelWidth - 15,onePixel + 4); g.setColor(Color.black); } // y axis for(int i = 0; i <= sectionCount; i++) { yPixel = leftXOffset + i*80; g.drawLine(yPixel,20,yPixel,drawingPanelHeight-10); } g.drawString("n", 5,xAxisPixel + 4); g.drawString("0",drawingPanelWidth - 15,xAxisPixel + 4); g.drawString("Y",yAxisPixel-2,17); } // drawAxis // draw limits in the proper places public void drawLimits(Graphics g) { int xAxisPixel, yAxisPixel, xMaxPixel, xMidPixel, yPixel; int xBasePixel, x1Pixel; double yVal; xAxisPixel = (int) yRealToPixel(0); if(xAxisPixel > drawingPanelHeight - 50) xAxisPixel = drawingPanelHeight - 50; if(xAxisPixel < 28) xAxisPixel = 28; yAxisPixel = leftXOffset; xMidPixel = leftXOffset + 200; xMaxPixel = leftXOffset + 400; g.setColor(Color.red); // x limit for (int i = 0; i <= sectionCount/2; i++) { g.drawString(Integer.toString(nGraph + i*40*nStep), yAxisPixel + 4 +i*160 ,xAxisPixel + 14); g.drawLine(yAxisPixel,xAxisPixel +i*160 - 2, yAxisPixel,xAxisPixel +i*160 + 2); } // y limit yVal = yMax; yPixel = yRealToPixel(yVal); g.drawString(Mio.doubleToStringSigFigures(yVal, 4), yAxisPixel - 40,yPixel + 3); g.drawLine(yAxisPixel - 2,yPixel, yAxisPixel + 2,yPixel); yVal = yMin; yPixel = yRealToPixel(yVal); g.drawString(Mio.doubleToStringSigFigures(yVal, 4), yAxisPixel - 40,yPixel + 3); g.drawLine(yAxisPixel - 2,yPixel, yAxisPixel + 2,yPixel); } // drawLimits // input real value, output pixel in interval [0, YSCALE] public int yRealToPixel(double yReal) { return (int)((yReal - yMax) * (drawingPanelHeight - 3*YOFFSET) / (yMin - yMax) + YOFFSET); } // yRealToPixel public int yRealToPixel(int yReal) { return (int)((yReal - yMax) * (drawingPanelHeight - 3*YOFFSET) / (yMin - yMax) + YOFFSET); } // yRealToPixel // input pixel, output real in interval [yMin, yMax] public double yPixelToReal(double yPixel) { return ((yPixel - YOFFSET) * (yMin - yMax) / (drawingPanelHeight - 3*YOFFSET) + yMax); } // yPixelToReal public double yPixelToReal(int yPixel) { return ((yPixel - YOFFSET) * (yMin - yMax) / (drawingPanelHeight - 3*YOFFSET) + yMax); } // yPixelToReal } // DrawingPanel class // NFunction Class /* Modified by Mike May, August 2001 * * *public class NFunction * public NFunction() * public void parse( String f ) * * The class NFuncttion is a is used to evaluate a function in n. * the parse method sends the string over to the Parser class for parsing * * * public double value(double n) * * These method evaluates the function at n. */ // java packages import java.applet.*; import java.awt.*; import java.lang.Math; import expr.*; // expr package Copyright 1996 by Darius Bacon public class NFunction { // created for access to infinity constants Double doubleCheck = new Double(1.0); public Expr expr; public Variable nvar; public NFunction() { // set up the parsing variables nvar = Variable.make("n"); 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 */ } // NFunction 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 n) { nvar.set_value(n); return expr.value(); } // value } // end of NFunction Class /* * The feedback panel is intended to contain all the inputs and text * outputs used by the applet. * *public class FeedbackPanel extends Panel * public FeedbackPanel(SeqSeries seqSeries) * public void collectData() * public void internalFeedbackPanelComputations() * public String evalTerm(String name, NFunction f, int m) * public String evalSum(String name, NFunction f, int m) * public void update() * public void getInputValues() * public void sendInputValues() * public void getDomainValues() * public void sendDomainValues() * public void getControlValues() * public void sendOutputValues() * * It is broken into four subpanels, one each for: user supplied inputs, * the domain and range of the graph, control values of what will be graphed, * and for output. There may be a get and send method for each subpanel. * The fixInputs method collects data from all panels. The * internalFeedbackPanelComputations method does the obvious things. * The methods evalTerm and evalSum are subsidiary methods to * internalFeedbackPanelComputations. * The update method is there to invoke methods in the calling applet * that contains the FeedbackPanel. * * *public class InputPanel extends Panel * public InputPanel(FeedbackPanel fPanel) * public boolean action(Event e, Object arg) * public void collectInputValues() * public void postInputValues() * *public class ControlPanel extends Panel * public ControlPanel(FeedbackPanel fPanel) * class SymItem implements java.awt.event.ItemListener * public void itemStateChanged(java.awt.event.ItemEvent event) * public void collectControlValues() * *public class OutputPanel extends Panel * public OutputPanel() * public void postOutput() * *public class DomainPanel extends Panel * public DomainPanel(FeedbackPanel fPanel) * public boolean action(Event e, Object arg) * public void postDomainValues() * public void collectDomainValues() * * * In addition to its constructor, each of the four subpanels may have an action * listener and methods to collect and post data. */ import java.awt.*; import java.applet.*; import MIO.*; public class FeedbackPanel extends Panel { /* * Public variables that are either inputs or outputs to be passed */ boolean termsState, sumsState, ratioState, pointsState, linesState; boolean aNState, bNState, geomState, harmState, expState; int nStart = 1, nGraph = 1, nStep, toViewValue; String aNString, bNString, geomString, harmString, expString; String string1a, string1b, string2a, string2b, string3a, string3b, string4a, string4b, string5a, string5b; double aNSum, bNSum, geomSum, harmSum, kharmSum; double yMin = -5.0, yMax = 5.0; NFunction nFunc; // ZFunction f; InputPanel inputPanel; ControlPanel controlPanel; OutputPanel outputPanel; DomainPanel domainPanel; SeqSeries applet; public FeedbackPanel(SeqSeries sumSeries) { this.setLayout(null); applet = sumSeries; inputPanel = new InputPanel(this); inputPanel.setBounds(0,0,200,120); inputPanel.setLayout(null); this.add(inputPanel); outputPanel = new OutputPanel(); outputPanel.setBounds(0,120,200,160); outputPanel.setLayout(new GridLayout(0,1,0,0)); this.add(outputPanel); controlPanel = new ControlPanel(this); controlPanel.setBounds(0,280,200,60); controlPanel.setLayout(null); this.add(controlPanel); domainPanel = new DomainPanel(this); domainPanel.setBounds(0, 340, 200, 60); domainPanel.setLayout(null); this.add(domainPanel); nFunc = new NFunction(); } // FeedbackPanel // Gather the inputs - main collection method public void collectData() { getInputValues(); getDomainValues(); getControlValues(); internalFeedbackPanelComputations(); sendOutputValues(); } // collectData public void internalFeedbackPanelComputations() { string1a = " "; string1b = " "; if(aNState == true) { nFunc.parse(aNString); string1a = evalTerm("A",nFunc,toViewValue); string1b = evalSum("A",nFunc,toViewValue); } string2a = " "; string2b = " "; if(bNState == true) { nFunc.parse(bNString); string2a = evalTerm("B",nFunc,toViewValue); string2b = evalSum("B",nFunc,toViewValue); } string3a = " "; string3b = " "; if(geomState == true) { nFunc.parse(geomString); string3a = evalTerm("G",nFunc,toViewValue); string3b = evalSum("G",nFunc,toViewValue); } string4a = " "; string4b = " "; if(harmState == true) { nFunc.parse(harmString); string4a = evalTerm("H",nFunc,toViewValue); string4b = evalSum("H",nFunc,toViewValue); } string5a = " "; string5b = " "; if(expState == true) { nFunc.parse(expString); string5a = evalTerm("E",nFunc,toViewValue); string5b = evalSum("E",nFunc,toViewValue); } } // internalFeedbackPanelComputations public String evalTerm(String name, NFunction f, int m) { double temp = f.value(m); if(!(temp <= Double.POSITIVE_INFINITY)) temp = 0.0; return "("+name+", "+m+") = "+ Mio.doubleToStringSigFigures(temp,7) ; } // evalTerm public String evalSum(String name, NFunction f, int m) { double sum = 0.0, temp = 0.0; for(int i = nStart; i <= toViewValue; i++) { temp = f.value(i); if(!(temp <= Double.POSITIVE_INFINITY)) temp = 0; sum += temp; } return "S("+name+", "+m+") = "+ Mio.doubleToStringSigFigures(sum, 7); } // evalSum public void update() { applet.fixMain(); } // update // Methods for communicating with subsidiary objects public void getInputValues() { inputPanel.collectInputValues(); aNState = inputPanel.aNState; bNState = inputPanel.bNState; geomState = inputPanel.geomState; harmState = inputPanel.harmState; expState = inputPanel.expState; aNString = inputPanel.aNString; bNString = inputPanel.bNString; geomString = inputPanel.geomString; harmString = inputPanel.harmString; expString = inputPanel.expString; toViewValue = inputPanel.toViewValue; } // getInputValues public void sendInputValues() { inputPanel.postInputValues(); } // sendInputValues public void getDomainValues() { domainPanel.collectDomainValues(); nStart = domainPanel.nStart; nGraph = domainPanel.nGraph; nStep = domainPanel.nStep; yMin = domainPanel.yMin; yMax = domainPanel.yMax; } // getDomainValues public void sendDomainValues() { domainPanel.postDomainValues(); } // sendDomainValues public void getControlValues() { controlPanel.collectControlValues(); termsState = controlPanel.termsState; sumsState = controlPanel.sumsState; ratioState = controlPanel.ratioState; pointsState = controlPanel.pointsState; linesState = controlPanel.linesState; } // getControlValues public void sendOutputValues() { outputPanel.string1a = string1a; outputPanel.string1b = string1b; outputPanel.string2a = string2a; outputPanel.string2b = string2b; outputPanel.string3a = string3a; outputPanel.string3b = string3b; outputPanel.string4a = string4a; outputPanel.string4b = string4b; outputPanel.string5a = string5a; outputPanel.string5b = string5b; outputPanel.postOutput(); } //sendOutputValues } // FeedbackPanel class //// Start new subsidiary class public class InputPanel extends Panel { /* * Public variables that are either inputs or outputs to be passed */ boolean aNState, bNState, geomState, harmState, expState; String aNString, bNString, geomString, harmString, expString; double geoConstant, geoBase, harmConstant, expBase; int toViewValue; /* * GUI elements for the panel */ Label aNLabel, bNLabel, geomLabel, geomLabel2, geomLabel3, harmLabel, harmLabel2, expLabel, expLabel2, toViewLabel; TextField aNField, bNField, geomField, geomField2, harmField, expField, toViewField; Checkbox aNBox, bNBox, geomBox, harmBox, expBox; FeedbackPanel feedbackPanel; public InputPanel(FeedbackPanel fPanel) { feedbackPanel = fPanel; aNBox = new Checkbox(null, true); aNBox.setBounds(0,0,18,18); aNBox.setBackground(Color.red); this.add(aNBox); aNLabel = new Label("A(n) = "); aNLabel.setBounds(20,0,35,18); this.add(aNLabel); aNField = new TextField("3/n^2", 30); aNField.setBounds(60,0,140,18); this.add(aNField); bNBox = new Checkbox(null, true); bNBox.setBounds(0,20,18,18); bNBox.setBackground(Color.blue); this.add(bNBox); bNLabel = new Label("B(n) = "); bNLabel.setBounds(20,20,35,18); this.add(bNLabel); bNField = new TextField("(-1)^n/n", 30); bNField.setBounds(60,20,140,18); this.add(bNField); geomBox = new Checkbox(null, true); geomBox.setBounds(0,40,18,18); geomBox.setBackground(Color.green); this.add(geomBox); geomLabel = new Label("G(n) = "); geomLabel.setBounds(20,40,35,18); this.add(geomLabel); geomField = new TextField("2.5", 30); geomField.setBounds(60,40,40,18); this.add(geomField); geomLabel2 = new Label(" * "); geomLabel2.setBounds(100,40,20,18); this.add(geomLabel2); geomField2 = new TextField(".75", 30); geomField2.setBounds(120,40,40,18); this.add(geomField2); geomLabel3 = new Label(" ^n"); geomLabel3.setBounds(160,40,20,18); this.add(geomLabel3); harmBox = new Checkbox(null, true); harmBox.setBounds(0,60,18,18); harmBox.setBackground(Color.magenta); this.add(harmBox); harmLabel = new Label("H(n) = "); harmLabel.setBounds(20,60,35,18); this.add(harmLabel); harmField = new TextField("2", 30); harmField.setBounds(60,60,40,18); this.add(harmField); harmLabel2 = new Label(" * 1/n "); harmLabel2.setBounds(100,60,40,18); this.add(harmLabel2); expBox = new Checkbox(null, true); expBox.setBounds(0,80,18,18); expBox.setBackground(Color.cyan); this.add(expBox); expLabel = new Label("E(n) = "); expLabel.setBounds(20,80,35,18); this.add(expLabel); expField = new TextField("ln(3)", 30); expField.setBounds(60,80,40,18); this.add(expField); expLabel2 = new Label(" ^n/fact(n) "); expLabel2.setBounds(100,80,60,18); this.add(expLabel2); toViewLabel = new Label("n to view = "); toViewLabel.setBounds(0,100,55,18); this.add(toViewLabel); toViewField = new TextField("100", 30); toViewField.setBounds(60,100,100,18); this.add(toViewField); SymItem lSymItem = new SymItem(); aNBox.addItemListener(lSymItem); bNBox.addItemListener(lSymItem); geomBox.addItemListener(lSymItem); harmBox.addItemListener(lSymItem); expBox.addItemListener(lSymItem); } // InputPanel // handle GUI events // listen for changes in checkboxes class SymItem implements java.awt.event.ItemListener { public void itemStateChanged(java.awt.event.ItemEvent event) { feedbackPanel.collectData(); feedbackPanel.update(); } } // SymItem class // listen for button pushes and returns in text fields public boolean action(Event e, Object arg) { if( (e.target == aNField)||(e.target == bNField)|| (e.target == geomField)||(e.target == geomField2)||(e.target == harmField)|| (e.target == expField)||(e.target == toViewField) ) { feedbackPanel.collectData(); feedbackPanel.update(); return true; } else return super.action(e,arg); } // action // Gather the inputs - main collection method public void collectInputValues() { aNString = aNField.getText(); bNString = bNField.getText(); geoConstant = Mio.doubleFromTextField(geomField); if(geoConstant == 0) geomField.setText("0"); geoBase = Mio.doubleFromTextField(geomField2); if(geoBase == 0) geomField2.setText("0"); geomString = geomField.getText() + "*(" + geomField2.getText() + ")^n"; harmConstant = Mio.doubleFromTextField(harmField); if(harmConstant == 0) harmField.setText("0"); harmString = harmField.getText() + "/n"; expBase = Mio.doubleFromTextField(expField); if(expBase == 0) expField.setText("0"); expString = "(" + expField.getText() + ")^n/fact(n)"; toViewValue = Mio.intFromTextField(toViewField); if(toViewValue == 0) toViewField.setText("0"); aNState = aNBox.getState(); bNState = bNBox.getState(); geomState = geomBox.getState(); harmState = harmBox.getState(); expState = expBox.getState(); } // collectInputValues public void postInputValues() { } // postInputValues } // InputPanel class //// Start new subsidiary class public class ControlPanel extends Panel { /* * Public variables that are either inputs or outputs to be passed */ boolean termsState, sumsState, ratioState, pointsState, linesState; /* * GUI elements for the panel */ Checkbox termsBox, sumsBox, ratioBox, pointsBox, linesBox; FeedbackPanel feedbackPanel; public ControlPanel(FeedbackPanel fPanel) { feedbackPanel = fPanel; termsBox = new Checkbox("Terms ", false); termsBox.setBounds(0,0,60,18); this.add(termsBox); sumsBox = new Checkbox("Sums ", true); sumsBox.setBounds(70,0,60,18); this.add(sumsBox); ratioBox = new Checkbox("Ratios ", false); ratioBox.setBounds(140,0,60,18); this.add(ratioBox); CheckboxGroup printGroup = new CheckboxGroup(); pointsBox = new Checkbox("Points ", printGroup, true); pointsBox.setBounds(0,20,90,18); this.add(pointsBox); linesBox = new Checkbox("Lines ", printGroup, false); linesBox.setBounds(100,20,90,18); this.add(linesBox); SymItem lSymItem = new SymItem(); termsBox.addItemListener(lSymItem); sumsBox.addItemListener(lSymItem); ratioBox.addItemListener(lSymItem); pointsBox.addItemListener(lSymItem); linesBox.addItemListener(lSymItem); } // ControlPanel // handle GUI events // listen for changes in menues class SymItem implements java.awt.event.ItemListener { public void itemStateChanged(java.awt.event.ItemEvent event) { feedbackPanel.collectData(); feedbackPanel.update(); } } // SymItem class // Gather the inputs - main collection method public void collectControlValues() { termsState = termsBox.getState(); sumsState = sumsBox.getState(); ratioState = ratioBox.getState(); pointsState = pointsBox.getState(); linesState = linesBox.getState(); } // collectControlValues } // ControlPanel class //// Start new subsidiary class public class OutputPanel extends Panel { /* * Public variables that are either inputs or outputs to be passed */ String string1a = " ", string1b = " ", string2a = " ", string2b = " ", string3a = "3a", string3b = "3b", string4a = "4a", string4b = " ", string5a = " ", string5b = " "; /* * GUI elements for the panel */ Label label1a, label1b, label2a, label2b, label3a, label3b, label4a, label4b, label5a, label5b; public OutputPanel() { label1a = new Label(); label1a.setForeground(Color.red); this.add(label1a); label1b = new Label(); label1b.setForeground(Color.red); this.add(label1b); label2a = new Label(); label2a.setForeground(Color.blue); this.add(label2a); label2b = new Label(); label2b.setForeground(Color.blue); this.add(label2b); label3a = new Label(); label3a.setForeground(Color.green); this.add(label3a); label3b = new Label(); label3b.setForeground(Color.green); this.add(label3b); label4a = new Label(); label4a.setForeground(Color.magenta); this.add(label4a); label4b = new Label(); label4b.setForeground(Color.magenta); this.add(label4b); label5a = new Label(); label5a.setForeground(Color.cyan); this.add(label5a); label5b = new Label(); label5b.setForeground(Color.cyan); this.add(label5b); } // OutputPanel public void postOutput() { // Adjust with round to get desired precision label1a.setText(string1a); label1b.setText(string1b); label2a.setText(string2a); label2b.setText(string2b); label3a.setText(string3a); label3b.setText(string3b); label4a.setText(string4a); label4b.setText(string4b); label5a.setText(string5a); label5b.setText(string5b); } // postOutput } // class OutputPanel //// Start new subsidiary class public class DomainPanel extends Panel // Unchanged { /* * Public variables that are either inputs or outputs to be passed */ double yMin = -5.0, yMax = 5.0; int nStart = 1, nGraph = 1, nStep = 1; /* * Variables that are used in internal computations */ double xCenter, halfWide, yCenter, halfHigh; /* * GUI elements for the panel */ Button yIn, yOut; // Button zoomIn, zoomOut, yIn, yOut; Label nStartLabel, nGraphLabel, nStepLabel, yMinLabel, yMaxLabel; TextField nStartField, nGraphField, nStepField, yMinField, yMaxField; FeedbackPanel feedbackPanel; public DomainPanel(FeedbackPanel fPanel) { this.setLayout(null); feedbackPanel = fPanel; // Assume panel is 200 by 60 nStartLabel = new Label("n Start ="); nStartLabel.setBounds(0, 0, 45, 18); this.add(nStartLabel); nStartField = new TextField("1", 7); nStartField.setBounds(50, 0, 45, 18); this.add(nStartField); nGraphLabel = new Label("nGraph ="); nGraphLabel.setBounds(0, 20, 45, 18); this.add(nGraphLabel); nGraphField = new TextField("1", 7); nGraphField.setBounds(50, 20, 45, 18); this.add(nGraphField); nStepLabel = new Label("n Step ="); nStepLabel.setBounds(0, 40, 45, 18); this.add(nStepLabel); nStepField = new TextField("1", 7); nStepField.setBounds(50, 40, 45, 18); this.add(nStepField); yIn = new Button("Y In"); yIn.setBounds(105,40,40,14); this.add(yIn); yOut = new Button("Y Out"); yOut.setBounds(150,40,40,14); this.add(yOut); yMaxLabel = new Label("yMax ="); yMaxLabel.setBounds(100, 0, 40, 18); this.add(yMaxLabel); yMaxField = new TextField("10.0", 7); yMaxField.setBounds(140, 0, 60, 18); this.add(yMaxField); yMinLabel = new Label("yMin ="); yMinLabel.setBounds(100, 20, 40, 18); this.add(yMinLabel); yMinField = new TextField("-10.0", 7); yMinField.setBounds(140, 20, 60, 18); this.add(yMinField); } // DomainPanel // listen for button pushes and returns in text fields public boolean action(Event e, Object arg) { if((e.target == nStartField)||(e.target == nGraphField)|| (e.target == nStepField)||(e.target == yMinField)|| (e.target == yMaxField)) { feedbackPanel.collectData(); feedbackPanel.update(); return true; } if(e.target == yIn) { yCenter = (yMax + yMin)/2; halfHigh = yMax - yCenter; yMin = yCenter - halfHigh/2.0; yMax = yCenter + halfHigh/2.0; postDomainValues(); feedbackPanel.collectData(); feedbackPanel.update(); return true; } if(e.target == yOut) { yCenter = (yMax + yMin)/2; halfHigh = yMax - yCenter; yMin = yMin - halfHigh; yMax = yMax + halfHigh; postDomainValues(); feedbackPanel.collectData(); feedbackPanel.update(); return true; } else return super.action(e,arg); } // action public void postDomainValues() { nStartField.setText(String.valueOf(nStart)); nGraphField.setText(String.valueOf(nGraph)); nStepField.setText(String.valueOf(nStep)); yMinField.setText(Mio.doubleToStringSigFigures(yMin,5)); yMaxField.setText(Mio.doubleToStringSigFigures(yMax,5)); } // postDomainValues public void collectDomainValues() { nStart=Mio.intFromTextField(nStartField); nGraph=Mio.intFromTextField(nGraphField); if((nGraph < nStart)||(nGraph > nStart + 100000)) { System.out.println("Warning, nGraph must be between"); System.out.println("nStart and nStart + 100,000"); nGraph = nStart; nGraphField.setText(Integer.toString(nGraph)); } nStep=Mio.intFromTextField(nStepField); if((nStep < 1)||(nStep > 1000)) { System.out.println("Warning, nStep must be between"); System.out.println("1 and 1000"); nStep = 1; nStepField.setText(Integer.toString(nStep)); } // 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; } postDomainValues(); } // collectDomainValues } // DomainPanelClass // End of FeedbackPanel File /* * 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 is designed to explore sequences and series and they would be\n" + "used in a typical calculus course. They can be explored both numerically\n" + "by looking at the value at a particular n and graphically, looking at 100\n" + "values together.\n\n" + "The applet lets the user choose to see graphs of terms, or sums, or ratios of terms.\n\n" + "The obvious first point is that convergent series typically have graphs that look \n" + "convergent while divergent series never do settle down.\n\n" + "The user can set nStart, where the sequence starts, nGraph, where the graph starts,\n" + "and nStep, the distance between graphical steps. The value of nGraph should be \n" + "within 100,000 of nStart. The value of nStep must be between 1 and 1,000.\n\n" + "The applet will display information on up to 5 sequences. The first two of the\n" + "sequences are user defined. For the remaining three, the user sets parameters\n" + "for sequences that typically are of importance in the study of sequences and\n" + "series, namely a geometric sequence, a harmonic sequence, and a sequence for \n" + "the Taylor series of the exponential function.\n\n" + "The two user specified sequences should be expressions in n.\n\n" + "The function should be an expression in n. 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, fact, 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.\n\n" + "It should be noted that the applet cheats on some computations.\n" + "Standard sequences often include terms like (c^n)/(n!) which becomes of\n" + "quotient of terms outside the bounds for java computations on decimal values.\n" + "This applet considers such terms to be zero."); 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 = "Sequence and Series Applet\n \n"+ "Copyright 2001 by Mike May, S.J.\n"+ "maymk@slu.edu\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 //// //// // 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 // 05AUG2001 - Mike May, S.J. // add in SEC, CSC. ToDEG, TORAD, restore FACT 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 = 119; 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 '-' 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, fact package expr; import java.io.*; /** Parses strings representing mathematical formulas with variables. The following operators, in descending order of precedence, are defined: ^ associates right-to-left; other operators associate left-to-right.

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

Whitespace outside identifiers is ignored.

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.

Examples: