// Riemann sums, coded by David Protas, c.2004
// Any corrections or suggestions for improvement of this code will be 
// appreciated and should be sent to david.protas@csun.edu
// Latest revision: April 28, 2004

/*********************
* Document: RS.java 
*********************/

import java.applet.*;
import java.awt.*;
import expr.*;

public class RS extends Applet {

   final int K = 400; // Number of points used to draw graph
   final int MaxN = 100; //Maximum number of subintervals
   double aD, bD, ymin, ymax, h, sum, total;
   int nI;
   boolean firstApprox;
   String functionString, partitionChoice, testPtChoice;
   double[] xarray = new double[K+1];
   double[] yarray = new double[K+1];
   double[] xArray = new double[MaxN+1];
   double[] yArray = new double[MaxN+1];
   double[] tArray = new double[MaxN+1];
   Panel pan2, bottom, subpan, pan4, pan6;
   Label functionLabel, aLabel, bLabel, nLabel, partitionLabel, testPtLabel, message;
   TextField functionField, aField, bField, nField;
   Choice  partition, testPt;
   TextArea results;
   Button plot, compute;
   RSGraph graph;
   Color babyBlue;
   
   public void init() {
      setLayout(new BorderLayout(4,1));
      bottom = new Panel();
      bottom.setLayout(new GridLayout(2,1));
      pan2 = new Panel();
      pan2.setLayout(new FlowLayout(1,8,2));
      pan4 = new Panel();
      pan4.setLayout(new FlowLayout(1,8,2));
      subpan = new Panel();
      pan6 = new Panel();
      pan6.setLayout(new GridLayout(2,1));
      functionLabel = new Label("f(x) = ");
      functionField = new TextField(20);
      aLabel = new Label("  a = ");
      bLabel = new Label("  b = ");
      nLabel = new Label("  n = ");
      partitionLabel = new Label(" Partition: ");
      testPtLabel = new Label(" Test Pts: ");
      aField = new TextField(5);
      bField = new TextField(5);
      nField = new TextField(5);
      partition = new Choice();
      partition.addItem(" regular ");
      partition.addItem("irregular");
      testPt = new Choice();
      testPt.addItem("left endpt ");
      testPt.addItem("right endpt");
      testPt.addItem(" midpoint  ");
      testPt.addItem(" irregular ");
      plot = new Button("Plot");
      compute = new Button("Compute");
      results = new TextArea("                                  ", 6, 50);
      results.setEditable(false);
      message = new Label("                                                  " +
                          "                               ");
      graph = new RSGraph();
      babyBlue = new Color(204,255,255);
    
      pan2.add(functionLabel);
      pan2.add(functionField);
      pan2.add(aLabel);
      pan2.add(aField);
      pan2.add(bLabel);
      pan2.add(bField);
      pan2.add(plot);
      add("North",pan2);
      add("Center",graph);
      pan4.add(nLabel);
      pan4.add(nField);
      pan4.add(partitionLabel);
      pan4.add(partition);
      pan4.add(testPtLabel);
      pan4.add(testPt);
      pan6.add(pan4);
      subpan.add(compute);
      subpan.add(message);
      pan6.add(subpan);
      bottom.add(pan6);
      bottom.add(results);
      add("South",bottom);
      
      setBackground(babyBlue);
      pan2.setBackground(babyBlue);
      pan4.setBackground(babyBlue);
      subpan.setBackground(babyBlue);
      pan6.setBackground(babyBlue);
      message.setForeground(Color.red);
      message.setBackground(babyBlue);
      results.setBackground(babyBlue);
      results.setFont(new Font("Courier",Font.PLAIN,10));
   }
    
   public boolean action(Event evt, Object arg) {
      Variable x = null;
      Expr function = null, aInput = null, bInput = null;
      if (evt.target == plot) {

         message.setText("");
         firstApprox = true;
         try { 
            x = Variable.make ("x");
            function = Parser.parse (functionField.getText());
            aInput = Parser.parse(aField.getText());
            bInput = Parser.parse(bField.getText());
         }
         catch (Syntax_error e) {
            message.setText("" + e);
         }
         Variable.make ("pi").set_value (Math.PI);
         Variable.make ("e").set_value (Math.E);
         functionString = functionField.getText();
         aD = aInput.value();
         bD = bInput.value();
         if (aD < bD) {
            results.setText(" f(x) = " + functionString + ", a = " + aD + ", b = " + bD);
            graph.aD = aD;
            graph.bD = bD;
            ymin = -.0000001;
            ymax = .0000001;
            for (int i = 0; i <= K; i++) {
               xarray[i] = aD + i*(bD - aD)/K;  //Array of x values
               x.set_value(xarray[i]);
               yarray[i] = function.value();    //Array of y values
               graph.xarray[i] = xarray[i];
               graph.yarray[i] = yarray[i];
               if (yarray[i] < ymin)
                  ymin = yarray[i];       //find min value of y
               if (yarray[i] > ymax)
                  ymax = yarray[i];       //find max value of y
            }
            graph.ymin = ymin;
            graph.ymax = ymax;
            graph.phase = 0;
            graph.repaint();
         } //end of if aD < bD
         else
            message.setText("Need a < b. Try again.");
         return true;
      } //end of evt.target == plot
      if (evt.target == compute) {
         sum = 0;
         try {
            x = Variable.make ("x");
            function = Parser.parse (functionField.getText());
         }
         catch (Syntax_error e) {
            message.setText("" + e);
         }
         graph.phase = 1;
         if (entryValid(nField.getText()) == false)
            message.setText("n needs to be an integer. Try again.");
         else { 
            nI = intFromString(nField.getText());
            if (nI < 1)
               message.setText("n needs to be positive. Try again.");
            else if (nI > MaxN)
               message.setText("Applet requires n <= " + MaxN + ". Try again.");
            else {               // if n is OK
               message.setText("");
               graph.nI = nI;
               partitionChoice = partition.getSelectedItem();
               if (partitionChoice == " regular ") {
                  h = (bD - aD)/nI;
                  for (int i = 0; i <= nI; i++) {
                     xArray[i] = aD + i*h;
                     graph.xArray[i] = xArray[i];
                  }
               }
               else {
                  total = 0;
                  for (int i = 1; i <= nI; i++) {
                     xArray[i] = Math.random();   // length of interval i
                     total = total + xArray[i];
                     }
                  xArray[0] = aD;
                  graph.xArray[0] = xArray[0];
                  for (int i = 1; i < nI; i++) {
                     xArray[i] = xArray[i-1] + xArray[i]*(bD - aD)/total;
                     graph.xArray[i] = xArray[i];  // rt endpt of interval i
                  }
                  xArray[nI] = bD;
                  graph.xArray[nI] = xArray[nI];
               }                               // end of setting up partition
               testPtChoice = testPt.getSelectedItem();
               if (testPtChoice == "left endpt ") {
                  for (int i = 1; i <= nI; i++) {
                     tArray[i] = xArray[i-1];
                     x.set_value(tArray[i]);  // sets argument = tArray[i]
                     yArray[i] = function.value();   //computes f(t)
                     graph.yArray[i] = yArray[i];
                     sum = sum + yArray[i]*(xArray[i] - xArray[i-1]);
                  }
               }
               else if (testPtChoice == "right endpt") {
                  for (int i = 1; i <= nI; i++) {
                     tArray[i] = xArray[i];
                     x.set_value(tArray[i]);
                     yArray[i] = function.value();
                     graph.yArray[i] = yArray[i];
                     sum = sum + yArray[i]*(xArray[i] - xArray[i-1]);
                  }
               }
               if (testPtChoice == " midpoint  ") {
                  for (int i = 1; i <= nI; i++) {
                     tArray[i] = (xArray[i-1] + xArray[i])/2;
                     x.set_value(tArray[i]);
                     yArray[i] = function.value();
                     graph.yArray[i] = yArray[i];
                     sum = sum + yArray[i]*(xArray[i] - xArray[i-1]);
                  }
               }
               if (testPtChoice == " irregular ") {
                  for (int i = 1; i <= nI; i++) {
                     tArray[i] = xArray[i-1] + Math.random()*(xArray[i] - xArray[i-1]);
                     x.set_value(tArray[i]);
                     yArray[i] = function.value();
                     graph.yArray[i] = yArray[i];
                     sum = sum + yArray[i]*(xArray[i] - xArray[i-1]);
                  }
               }
               if (firstApprox == true) {
                  results.appendText("\n" +"  n    partition    testpoints       Riemann sum");
               }
               if (nI < 10) {
                  results.appendText("\n" +"  "+ nI + "    " + partitionChoice  + "    " +
                                    testPtChoice + "   " + rndOff(sum));
               }
               else {
                  results.appendText("\n" +" "+ nI + "    " + partitionChoice  + "    " +
                                    testPtChoice + "   " + rndOff(sum));
               }
               graph.repaint();
               firstApprox = false;
            } //end of else nI > MaxN, i.e. "if n is OK"
         } //end of else entryValid
         return true;
      } //end of evt.target == approx
      return false;    
   } //end of action
   
   public static int intFromString(String str) {
      Integer intObj = new Integer(str);
      return intObj.intValue();
   }
   
   private boolean entryValid(String entry) {
      boolean status;
      try {
         double number = intFromString(entry);
         status = true;
      }
      catch(NumberFormatException e) {
         status =false;
      }
      return status;
   }
   
public static String rndOff(double number)  //to 5 places past the decimal
   {
      String strnum, bigstrnum, substrnum = "     0.00000";
      int period, lngth;
      long longnum;
      
      if ((number >= 0.001) || (number <= -0.001) || (number != number)) {
         number = Math.pow(0.1,5)*Math.round(Math.pow(10,5)*number);
         strnum = String.valueOf(number);
         bigstrnum = "      " + strnum + "     ";
         period = bigstrnum.indexOf('.');
         substrnum = bigstrnum.substring(period -6, period + 6);
         if ((number >= 1000000) || (number <= -100000) || (number != number))
            substrnum = "big magnitude";
      }
      else {
         longnum = Math.round(Math.pow(10,5)*number);
         if (longnum == 0)
            substrnum = "     0.00000";
         else {
            strnum = String.valueOf(longnum);
            if (longnum < 0)
               strnum = strnum.substring(1);
            lngth = strnum.length();
            switch (lngth) {
               case 1:
                  substrnum = "     0.0000" + strnum;
                  break;
               case 2:
                  substrnum = "     0.000" + strnum;
                  break;
               default:
                  substrnum = "     error";
            }
            if (longnum < 0)
               substrnum = "    -" + substrnum.substring(5);
         }
      }
      return substrnum;
   }

}

/**************************
* Document: RSGraph.java 
**************************/

import java.awt.*;

public class RSGraph extends Canvas {

   final int K = 400;  //number of steps used to draw graph
   final int MaxN = 100; //Maximum number of subintervals
   
   Dimension d;   
   double[] xarray = new double[K+1];
   double[] yarray = new double[K+1];
   double[] xArray = new double[MaxN+1];
   double[] yArray = new double[MaxN+1];
   double[] tArray = new double[MaxN+1];
   double aD, bD, ymin, ymax;
   int phase = -1, tick, deltaTick, nI;
   
   public void paint(Graphics g) {
      if (phase == 1) {
         g.setColor(Color.pink);
         for (int i = 1; i <= nI; i++) {
            if (yArray[i] > 0) {
               g.fillRect(xScaler(xArray[i-1]), yScaler(yArray[i]), 
                          xScaler(xArray[i]) - xScaler(xArray[i-1]), 
                          yScaler(0.0) - yScaler(yArray[i]));
            }
            else {
               g.fillRect(xScaler(xArray[i-1]), yScaler(0.0), 
                          xScaler(xArray[i]) - xScaler(xArray[i-1]), 
                          yScaler(yArray[i]) - yScaler(0.0));
            }
         }
         g.setColor(Color.magenta);
         for (int i = 1; i <= nI; i++) {
            if (yArray[i] > 0) {
               g.drawRect(xScaler(xArray[i-1]), yScaler(yArray[i]), 
                          xScaler(xArray[i]) - xScaler(xArray[i-1]), 
                          yScaler(0.0) - yScaler(yArray[i]));
            }
            else {
               g.drawRect(xScaler(xArray[i-1]), yScaler(0.0), 
                          xScaler(xArray[i]) - xScaler(xArray[i-1]), 
                          yScaler(yArray[i]) - yScaler(0.0));
            }
         }       
      }  //end of if
      if (phase != -1) {
         d = this.size();
         g.setColor(Color.black);
         g.drawLine(0, yScaler(0.0), d.width, yScaler(0.0));   // x-axis
         if (aD*bD < 0)
            g.drawLine(xScaler(0.0), 0, xScaler(0.0), d.height - 6);  // y-axis
         tick = (int)aD;    // start of x-ticks
         deltaTick = 1 + (int)(bD - aD)/14;
         do {
            if (tick != 0) {
               g.drawString(tick+"", xScaler(tick) - 4, yScaler(0.0) + 11);
               g.drawLine(xScaler(tick), yScaler(0.0) - 2, xScaler(tick), yScaler(0.0));
            }
            tick = tick + deltaTick;
         }
         while (tick <= bD);  //end of x-ticks
         for (int j = 0; j < K; j++) {                          // y = f(x)
            g.drawLine(xScaler(xarray[j]), yScaler(yarray[j]), 
                       xScaler(xarray[j+1]), yScaler(yarray[j+1]));
         }
      }  //end of if
   }
   
   private int xScaler(double x)
   {  
      return (int)(5 + (d.width - 10)*(x - aD)/(bD - aD));
   }
   
   private int yScaler(double y)
   {
      return (int)((d.height - 11)*(ymax - y)/(ymax - ymin));
   } 
      
}

/**************************************
* Folder: expr    Document: Expr.java 
**************************************/

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

// 14May96: added constant folding
// 6June02: changes made by David Protas indicated by /*DP*/

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;
  
  /** 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 LN    = 114;   /*DP*/
  /** 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;

  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;
  }
}

// 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; }
}

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

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 FLOOR: return Math.floor (arg);
    case LN:    return Math.log (arg);                   /*DP*/
    case LOG:   return Math.log (arg)/Math.log (10);     /*DP*/
    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);
    default: throw new RuntimeException ("BUG: bad rator");
    }
  }
}

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);
    default: throw new RuntimeException ("BUG: bad rator");
    }
  }
}

/****************************************
* Folder: expr    Document: Parser.java 
****************************************/

// 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.

package expr;

import java.io.*;

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


  ^ (raise to a power)
  * /
  Unary minus (-x)
  + -


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

  These functions are defined: 
    abs, acos, asin, atan, 
    ceil, cos, exp, floor, (ln added by DP)
    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:
  
  42
  2-3
  cos(x^2) + sin(x^2)
  
 */
public class Parser {
  static StreamTokenizer tokens;

  public static Expr parse (String input) throws Syntax_error {
    tokens = new StreamTokenizer (new StringBufferInputStream (input));
    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 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 = 10; r = 11; rator = Expr.ADD; break;
      case '-': l = 10; r = 11; rator = Expr.SUB; 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", "asin", "atan", 
    "ceil", "cos", "exp", "floor", "ln",    // ln added by DP 
    "log", "round", "sin", "sqrt", 
    "tan"
  };
  static int[] rators = {
    Expr.ABS, Expr.ACOS, Expr.ASIN, Expr.ATAN, 
    Expr.CEIL, Expr.COS, Expr.EXP, Expr.FLOOR, Expr.LN,  // Expr.LN added by DP
    Expr.LOG, Expr.ROUND, Expr.SIN, Expr.SQRT, 
    Expr.TAN
  };
	
  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");
    }
  }
}

/**********************************************
* Folder: expr    Document: Syntax_error.java 
**********************************************/

// 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); }
}

/******************************************
* Folder: expr    Document: Variable.java 
******************************************/

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

// 01Jun96: made `make' synchronized.

package expr;

import java.util.Hashtable;

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

  /**
   * Return the variable named `_name'.  
   * make (s1) == make (s2) iff s1.equals (s2).
   */
  static public synchronized 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 = 0; }

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


Back to applet