import java.applet.*; import java.awt.*; import java.math.*; import java.net.URL; public class BigCalculator extends Applet implements Runnable { public static final BigInteger UNITY = new BigInteger("1"); public static final BigDecimal ZERO = new BigDecimal(0.0d); public static final BigDecimal QUARTER = new BigDecimal(0.25d); public static final BigDecimal HALF = new BigDecimal(0.5d); public static final BigDecimal ONE = new BigDecimal(1.0d); public static final BigDecimal TWO = new BigDecimal(2.0d); public static final BigDecimal TEN = new BigDecimal(10.0d); public static final BigDecimal ONE_EIGHTY = new BigDecimal(180.0d); public static final BigDecimal PI = set_PI(); public static final BigDecimal LN10 = set_LN10(); public static final int RHU = BigDecimal.ROUND_HALF_UP; Thread crunch = new Thread(this); boolean breakSignal = false; String commandString = "none" ; // serves as the "parameter" for run(). TextField display,digits_field,DRG_field; boolean firstDigit = true; BigDecimal operand1 = ZERO; String operator = "="; //Initial operator BigDecimal memory[]; Canvas busy; int digits; int default_digits = 80; boolean degrees = false; boolean inDigitsField = false; boolean clearButtonFlag = false; Button feedback_button; URL feedback_URL; //-------------------------------------------------------------------- public void init() { /* Initialize everybody */ digits = default_digits; memory = new BigDecimal[3]; memory[0] = ZERO; memory[1] = ZERO; memory[2] = ZERO; display = new TextField("0"); display.setEditable(false); display.setBackground(Color.black); display.setForeground(Color.green); digits_field = new TextField(Integer.toString(digits)); digits_field.setBackground(Color.gray); digits_field.setFont(new Font("Helvetica", Font.BOLD, 12)); DRG_field = new TextField("RAD"); DRG_field.setEditable(false); DRG_field.setBackground(Color.black); DRG_field.setForeground(Color.yellow); DRG_field.setFont(new Font("Helvetica", Font.PLAIN, 12)); busy = new Canvas(); GridBagLayout gb = new GridBagLayout(); this.setLayout(gb); this.setFont(new Font("Helvetica", Font.BOLD, 16)); this.setBackground(Color.darkGray); Label title_label = new Label("BIG CALCULATOR",Label.CENTER); title_label.setFont(new Font("Helvetica", Font.BOLD, 16)); title_label.setForeground(Color.white); Label subt_label = new Label("An arbitrary precision calculator", Label.CENTER); subt_label.setForeground(Color.white); Label digits_label = new Label("digits:",Label.RIGHT); digits_label.setForeground(Color.white); feedback_button = new Button("by Jason Tiscione"); feedback_button.setForeground(Color.white); feedback_button.setBackground(Color.darkGray); feedback_button.setFont(new Font("Helvetica", Font.ITALIC, 12)); try { feedback_URL = new URL("mailto:j_tiscione@geocities.com"); } catch (java.net.MalformedURLException exc) {} /* Lay everything out */ int NONE=GridBagConstraints.NONE; int BOTH=GridBagConstraints.BOTH; int CENTER=GridBagConstraints.CENTER; int HORIZONTAL=GridBagConstraints.HORIZONTAL; Color syscol = Color.lightGray; Color stdcalc = Color.gray; Color breakcol = Color.getHSBColor(0.01f,0.5f,0.6f); Color scicalc = Color.getHSBColor(0.9f,0.2f,0.5f); Color memcol = Color.getHSBColor(0.6f,0.2f,0.5f); Color trigcol = Color.getHSBColor(0.1f,0.2f,0.5f); try { addComponent(this,title_label,0,0,10,1,HORIZONTAL,CENTER,0,0,0); addComponent(this,subt_label,3,1,4,1,HORIZONTAL,CENTER,0,0,0); addComponent(this,display,0,2,10,1,HORIZONTAL,CENTER,0,0,0); addComponent(this,feedback_button,8,1,2,1,HORIZONTAL,CENTER,0,0,5); addComponent(this,digits_label,0,3,1,1,HORIZONTAL,CENTER,0,0,0); addComponent(this,digits_field,1,3,1,1,HORIZONTAL,CENTER,0,0,0); addButton(new Button ("roundoff"),2,3,2,1,syscol); addButton(new Button ("backspace"),4,3,2,1,syscol); addComponent(this,DRG_field,6,3,1,1,HORIZONTAL,CENTER,0,0,8); addButton(new Button ("break"),7,3,2,1,breakcol); addComponent(this,busy,9,3,1,1,HORIZONTAL,CENTER,15,15,0); addButton(new Button ("CE/C"),0,4,1,1,syscol); addButton(new Button ("\u00f7"),1,4,1,1,stdcalc); //division symbol addButton(new Button ("\u00d7"),2,4,1,1,stdcalc); //mult. symbol addButton(new Button ("-"),3,4,1,1,stdcalc); addButton(new Button ("e^x"),4,4,1,1,scicalc); addButton(new Button ("10^x"),5,4,1,1,scicalc); addButton(new Button ("M3+"),6,4,1,1,memcol); addButton(new Button ("M3-"),7,4,1,1,memcol); addButton(new Button ("M3R"),8,4,1,1,memcol); addButton(new Button ("M3C"),9,4,1,1,memcol); addButton(new Button ("7"),0,5,1,1,stdcalc); addButton(new Button ("8"),1,5,1,1,stdcalc); addButton(new Button ("9"),2,5,1,1,stdcalc); addButton(new Button ("+"),3,5,1,2,stdcalc); addButton(new Button ("ln"),4,5,1,1,scicalc); addButton(new Button ("log"),5,5,1,1,scicalc); addButton(new Button ("M2+"),6,5,1,1,memcol); addButton(new Button ("M2-"),7,5,1,1,memcol); addButton(new Button ("M2R"),8,5,1,1,memcol); addButton(new Button ("M2C"),9,5,1,1,memcol); addButton(new Button ("4"),0,6,1,1,stdcalc); addButton(new Button ("5"),1,6,1,1,stdcalc); addButton(new Button ("6"),2,6,1,1,stdcalc); addButton(new Button ("sqrt"),4,6,1,1,scicalc); addButton(new Button ("rootn"),5,6,1,1,scicalc); addButton(new Button ("M1+"),6,6,1,1,memcol); addButton(new Button ("M1-"),7,6,1,1,memcol); addButton(new Button ("M1R"),8,6,1,1,memcol); addButton(new Button ("M1C"),9,6,1,1,memcol); addButton(new Button ("1"),0,7,1,1,stdcalc); addButton(new Button ("2"),1,7,1,1,stdcalc); addButton(new Button ("3"),2,7,1,1,stdcalc); addButton(new Button ("="),3,7,1,2,stdcalc); addButton(new Button ("1/x"),4,7,1,1,scicalc); addButton(new Button ("x^y"),5,7,1,1,scicalc); addButton(new Button ("pi"),6,7,1,1,trigcol); addButton(new Button ("arcsin"),7,7,1,1,trigcol); addButton(new Button ("arccos"),8,7,1,1,trigcol); addButton(new Button ("arctan"),9,7,1,1,trigcol); addButton(new Button ("0"),0,8,2,1,stdcalc); addButton(new Button ("."),2,8,1,1,stdcalc); addButton(new Button ("+/-"),4,8,1,1,scicalc); addButton(new Button ("x!"),5,8,1,1,scicalc); addButton(new Button ("R-D"),6,8,1,1,trigcol); addButton(new Button ("sin"),7,8,1,1,trigcol); addButton(new Button ("cos"),8,8,1,1,trigcol); addButton(new Button ("tan"),9,8,1,1,trigcol); } catch (AWTException e) { System.out.println("AWTException"); e.printStackTrace(); } crunch.start(); } //-------------------------------------------------------------------- public void destroy() { crunch.stop(); } //-------------------------------------------------------------------- public void start() { crunch.resume(); } //-------------------------------------------------------------------- public void stop() { crunch.suspend(); } //-------------------------------------------------------------------- public boolean mouseMove(Event e, int x, int y) { if (feedback_button.bounds().inside(x,y)) { this.showStatus("Email me questions, comments, bug reports, job offers..."); return true; } return false; } //-------------------------------------------------------------------- public boolean gotFocus(Event e, Object arg) { if (e.target == digits_field) { inDigitsField=true; } else { inDigitsField=false; return false; } return true; } //-------------------------------------------------------------------- public boolean lostFocus(Event e, Object arg) { if (e.target == digits_field) { int numberEntered; try { numberEntered = Integer.parseInt(digits_field.getText()); } catch (NumberFormatException exc) { // invalid entry numberEntered=default_digits; digits_field.setText(Integer.toString(numberEntered)); } if (numberEntered < 1) { // also an invalid entry numberEntered=default_digits; digits_field.setText(Integer.toString(numberEntered)); } digits = numberEntered; inDigitsField=false; } else { inDigitsField=true; return false; } return true; } //-------------------------------------------------------------------- public boolean action (Event e, Object arg) { if (e.target instanceof Button) { if (e.target==feedback_button) { try { this.getAppletContext().showDocument (feedback_URL, "_blank"); return true; } catch (Exception exc) { return true; } } String argstr = (String)arg; if (argstr.equalsIgnoreCase("break")) { breakSignal = true; } else { synchronized (commandString) { commandString = argstr; } } return true; } return false; // only if event was not a button press } //-------------------------------------------------------------------- public boolean keyDown(Event e, int key) { if((e.id==Event.KEY_PRESS) && (inDigitsField != true)) { synchronized (commandString) { switch (key) { case 10: // User pressed ENTER commandString = "="; break; case 8: // User pressed backspace key commandString = "backspace"; break; case 127: // User pressed delete key commandString = "backspace"; break; case 27: // User pressed escape key breakSignal = true; break; default: char dummyarg[] = { (char)key }; commandString = new String(dummyarg); } } return true; } return false; // only if event was not a keypress } //-------------------------------------------------------------------- public void run() { // crunch thread runs in an endless loop. If commandString is // set to a value by the main thread, crunch will grab it. // Therefore a synchronization lock is placed on all code in this // applet that reads or sets commandString. String s; while (true) { synchronized (commandString) { s=commandString; commandString = "none"; } if (!(s.equalsIgnoreCase("none"))) { light(true); discriminate(s); light(false); if (breakSignal) { // break key was hit- so do cleanup display.setText("0"); operator = "="; operand1 = ZERO; clearButtonFlag = false; firstDigit = true; breakSignal = false; } } } } //-------------------------------------------------------------------- private void discriminate(String s) { // if any key other than CE/C is pressed, clear clearButtonFlag if (!(s.equalsIgnoreCase("CE/C") ||s.equalsIgnoreCase("c"))) { clearButtonFlag = false; } if (s.equalsIgnoreCase("backspace")&&!firstDigit){ String disp = display.getText(); disp = disp.substring(0,disp.length()-1); if (disp.length()==0) { disp="0"; firstDigit=true; } display.setText(disp); } if ("0123456789.".indexOf(s) != -1) { // s contains either a digit or a decimal point if (firstDigit) { firstDigit=false; display.setText(s); } else { display.setText(display.getText() + s); } } else { String coplist = "= + - x * / \u00d7 \u00f7 x^y rootn"; if (coplist.indexOf(s) != -1) { //a conjunctive operator conjunctive (display.getText()); firstDigit = true; operator = s; } else { String uoplist = "C CE/C c M1+ M1- M1R M1C M2+ M2- M2R M2C" + " M3+ M3- M3R M3C +/- 1/x sqrt ln e^x log 10^x pi x!" + " sin cos tan arcsin arccos arctan R-D roundoff "; if (uoplist.indexOf(s) != -1) { //s is a unary operator unary (display.getText(),s); firstDigit = true; } } } } //-------------------------------------------------------------------- private void conjunctive (String disp) { BigDecimal operand2; try { operand2 = new BigDecimal(disp); } catch (NumberFormatException exc) { operand1 = ZERO; operand2 = ZERO; operator = "="; } char c=operator.charAt(0); if (c == '\u00d7') operator="x"; if (c == '*') operator = "x"; if (c == '\u00f7') operator="/"; if (operator.equalsIgnoreCase("=")) { operand1 = operand2; } if (operator.equalsIgnoreCase("+")) { operand1 = operand1.add(operand2); } if (operator.equalsIgnoreCase("-")) { operand1 = operand1.subtract(operand2); } if (operator.equalsIgnoreCase("x")) { operand1 = operand1.multiply(operand2); } if (operator.equalsIgnoreCase("/")) { if (operand2.signum()!=0) { operand1 = operand1.divide(operand2,digits,RHU); } } if (operator.equalsIgnoreCase("x^y")) { operand1 = pow(operand1,operand2); } if (operator.equalsIgnoreCase("rootn")) { operand1 = rootn(operand1,operand2); } display.setText (operand1.toString()); } //-------------------------------------------------------------------- private void unary (String disp, String op) { BigDecimal operand = new BigDecimal(disp); if (op.equalsIgnoreCase("CE/C")||op.equalsIgnoreCase("c")) { operand = ZERO; if (clearButtonFlag) { // second press of CE/C key operand1 = ZERO; operator = "="; } clearButtonFlag = true; } if (op.equalsIgnoreCase("1/x")) { if (operand.signum()!=0) { BigDecimal temp = ONE; operand = temp.divide(operand,digits,RHU); } } if (op.equalsIgnoreCase("+/-")) { operand = operand.negate(); } if (op.equalsIgnoreCase("sqrt")) { if (operand.signum() == 1) { operand = sqrt(operand); } } if (op.equalsIgnoreCase("e^x")) { operand = exp(operand); } if (op.equalsIgnoreCase("ln")) { if (operand.signum() == 1) { operand = ln(operand); } } if (op.equalsIgnoreCase("10^x")) { operand = alog(operand); } if (op.equalsIgnoreCase("log")) { if (operand.signum() == 1) { operand = log(operand); } } if (op.equalsIgnoreCase("x!")) { if (operand.compareTo(ONE.negate()) != -1) { operand = fact(operand); } } if (op.equalsIgnoreCase("R-D")) { degrees = !degrees; if (degrees) { DRG_field.setText("DEG"); } else { DRG_field.setText("RAD"); } } if (op.equalsIgnoreCase("pi")) { operand = pi(); } if (op.equalsIgnoreCase("sin")) { operand = sin(operand); } if (op.equalsIgnoreCase("cos")) { operand = cos(operand); } if (op.equalsIgnoreCase("tan")) { operand = tan(operand); } if (op.equalsIgnoreCase("arcsin")) { operand = arcsin(operand); } if (op.equalsIgnoreCase("arccos")) { operand = arccos(operand); } if (op.equalsIgnoreCase("arctan")) { operand = arctan(operand); } if (op.equalsIgnoreCase("roundoff")) { operand = operand.setScale(digits,RHU); } if (op.startsWith("M")) { int memindx=0; char c = op.charAt(1); // specifies which memory switch (c) { case '1': memindx = 0; break; case '2': memindx = 1; break; case '3': memindx = 2; break; } c = op.charAt(2); // specifies which memory operation switch (c) { case '+': memory[memindx] = memory[memindx].add(operand); break; case '-': memory[memindx] = memory[memindx].subtract(operand); break; case 'R': operand = memory[memindx]; break; case 'C': memory[memindx] = ZERO; break; } } display.setText (operand.toString()); } //-------------------------------------------------------------------- private void light (boolean status) { Graphics g=busy.getGraphics(); Rectangle r=busy.bounds(); int cx = r.width/2; int cy = r.height/2; if (status==true) { g.setColor(Color.red); g.fill3DRect(cx-5,cy-5,10,10,true); } else { g.setColor(Color.darkGray); g.fillRect(cx-5,cy-5,10,10); } } //-------------------------------------------------------------------- private void addButton(Button button, int gridx, int gridy, int gridwidth, int gridheight, Color c) { button.setBackground(c); button.setForeground(Color.black); Graphics g = this.getGraphics(); FontMetrics fm = g.getFontMetrics(); int textwidth = fm.stringWidth(button.getLabel()); int ipadx = (40-textwidth); int ipady = 0; int ext = 5; try { addComponent(this,button,gridx,gridy,gridwidth,gridheight, GridBagConstraints.BOTH,GridBagConstraints.CENTER,ipadx,0,ext); } catch (AWTException e) { System.out.println("AWTException"); e.printStackTrace(); } } //-------------------------------------------------------------------- private static void addComponent(Container container, Component component, int gridx, int gridy, int gridwidth, int gridheight, int fill, int anchor, int ipadx, int ipady, int extpad) throws AWTException { LayoutManager lm = container.getLayout(); if(!(lm instanceof GridBagLayout)) { throw new AWTException ("Invalid layout" + lm); } else { GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = gridx; gbc.gridy = gridy; gbc.weightx = gridx; gbc.weighty = gridy; gbc.gridwidth = gridwidth; gbc.gridheight = gridheight; gbc.fill = fill; gbc.anchor = anchor; gbc.ipadx = ipadx; gbc.ipady = ipady; gbc.insets = new Insets(extpad,extpad,extpad,extpad); ((GridBagLayout)lm).setConstraints(component, gbc); container.add(component); } } //-------------------------------------------------------------------- //-------------------------------------------------------------------- //--------------------- Numerical methods follow --------------------- //-------------------------------------------------------------------- //-------------------------------------------------------------------- private BigDecimal exp (BigDecimal x) { if (breakSignal) return ONE; if (x.signum()==-1) { return ONE.divide(exp(x.negate()),digits,RHU); } BigDecimal smallest = new BigDecimal(UNITY,digits); // 0.000...001 BigDecimal n = ZERO; BigDecimal term = ONE; BigDecimal acc_new = ONE; BigDecimal acc_old = ZERO; BigDecimal diff = acc_new.subtract(acc_old); while (diff.compareTo(smallest)==1) { if (breakSignal) return ONE; acc_old = acc_new; n = n.add(ONE); term = term.multiply(x); term = term.divide(n,digits,RHU); acc_new = acc_new.add(term); diff = term; } return acc_new.setScale(digits,RHU); } //-------------------------------------------------------------------- private BigDecimal ln (BigDecimal x){ if (breakSignal) return ONE; if ((x.compareTo(TEN)==0) && (digits<=1000)) { return LN10.setScale(digits,RHU); } if (x.compareTo(ONE)==1) { BigDecimal temp = ONE.divide(x,digits,RHU); temp = ln(temp); return (temp.negate()); } BigDecimal pointnine = new BigDecimal(0.9d); if (x.compareTo(pointnine) == -1) { digits++; BigDecimal temp = sqrt(x); if (breakSignal) return ONE; temp = ln(temp); digits--; temp = temp.add(temp); return (temp.setScale(digits,RHU)); } x = x.subtract(ONE); BigDecimal smallest = new BigDecimal(UNITY,digits); // 0.000...001 BigDecimal n = ZERO; BigDecimal term = ONE; BigDecimal powr = ONE; BigDecimal acc_new = ZERO; BigDecimal acc_old = ONE.negate(); BigDecimal diff = term.abs(); while (diff.compareTo(smallest)==1) { if (breakSignal) return ONE; acc_old=acc_new; n = n.add(ONE); powr = powr.multiply(x); powr = powr.setScale(digits,RHU); term = powr.divide(n,digits,RHU); acc_new = acc_new.add(term); powr = powr.negate(); diff = term.abs(); } return acc_new; } //-------------------------------------------------------------------- private BigDecimal alog (BigDecimal x){ if (x.scale()==0) { // x is an integer, so we can "cheat" return ONE.movePointRight(x.intValue()); } else { BigDecimal temp = x.multiply(LN10); temp = temp.setScale(digits,RHU); return exp(temp); } } //-------------------------------------------------------------------- private BigDecimal log (BigDecimal x){ if (x.compareTo(TEN)==0) { return ONE; } else { return (ln(x)).divide(ln(TEN),digits,RHU); } } //-------------------------------------------------------------------- private BigDecimal pow (BigDecimal x, BigDecimal y){ if (breakSignal) return ONE; if (x.signum() == 0) {return ZERO;} //get x=0 case out of the way if (y.scale()==0) { // if y is an integer, we can "cheat". int yint = y.intValue(); if (yint==0) {return x;} // get y=0 case out of the way BigDecimal product = ONE; BigDecimal multiplier = x; if (yint<0) { multiplier = ONE.divide(multiplier,digits,RHU); yint = -yint; } for (int i=0;i0 BigDecimal temp = y.multiply(ln(x)); temp = temp.setScale(digits,RHU); return exp(temp); } } return ZERO; // if thread reaches this point, args were no good. } //-------------------------------------------------------------------- private BigDecimal rootn (BigDecimal x, BigDecimal y){ if (y.signum()==0) return ZERO; // get rid of y=0 case if (y.scale()>0) { // y is not an integer. Send problem to pow(). return pow(x,ONE.divide(y,digits,RHU)); } return rootn(x,y.intValue()); //calls other version of rootn() } //-------------------------------------------------------------------- private BigDecimal rootn (BigDecimal a, int n){ if (breakSignal) return ONE; double double_n = (double)n; BigDecimal bigdec_n = new BigDecimal(double_n); BigDecimal c1 = new BigDecimal(double_n-1); c1 = c1.divide(bigdec_n,digits,RHU); BigDecimal c2 = a.divide(bigdec_n,digits,RHU); // algorithm is: let x = c1*x + c2/(x^powr) // where c1=(n-1)/n, c2=a/n, powr=n-1 BigDecimal smallest = new BigDecimal(UNITY,digits); // 0.000...001 BigDecimal xold = ZERO; //xxx just to initialize it BigDecimal xnew = ONE; //xxx 1 is as good a guess as any BigDecimal diff = xnew.subtract(xold); while (diff.compareTo(smallest)==1) { if (breakSignal) return ONE; xold = xnew; BigDecimal powerofx = ONE; for (int j=0;j