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:[email protected]"); }
    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;i<yint;i++){
        if (breakSignal) return ONE;
        product = product.multiply(multiplier);
      }
      return product;     
    }
    else {
      if (x.signum()==1) {  // require x>0
        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<n-1;j++) {
        if (breakSignal) return ONE;
        powerofx=powerofx.divide(xold,digits+3,RHU);
      }
      xnew = c1.multiply(xold); //first term.
      xnew = xnew.add(c2.multiply(powerofx)); // second term.
      diff = xnew.subtract(xold);
      diff = diff.abs();
    }
    if (breakSignal) return ONE;
    return xnew.setScale(digits,RHU);
  }

//--------------------------------------------------------------------
  private BigDecimal sqrt (BigDecimal a){
    if (breakSignal) return ONE;
    // algorithm is: let x = half * ( x + (a/x) )
    BigDecimal smallest = new BigDecimal(UNITY,digits); // 0.000...001
    BigDecimal xold = ZERO; // just to initialize it
    BigDecimal xnew = ONE; // 1 is as good a guess as any
    BigDecimal diff = xnew.subtract(xold);
    while (diff.compareTo(smallest)==1) {
      if (breakSignal) return ONE;
      xold = xnew;
      xnew = xold.add(a.divide(xold,digits+3,RHU)); // second term.
      xnew = xnew.divide(TWO,digits+3,RHU);
      diff = xnew.subtract(xold);
      diff = diff.abs();
    }
    return xnew.setScale(digits,RHU);
  }
//--------------------------------------------------------------------
  private BigDecimal fact(BigDecimal n) {
// Returns the factorial of a positive real argument n.
// If n is not an integer it returns gamma(1+n), which is defined to be
// an extension to the factorial from N to R, since for positive integers
// n!=gamma(1+n).
// The gamma function is not analytic for n=0,-1,-2,-3... and is not
// "well-behaved" in the negative real domain, so this method will only
// compute the function of arguments greater than -1.
   
    if (n.compareTo(ONE.negate())== -1) return ZERO;
    BigDecimal result = ONE; //xxx
    while (n.compareTo(ONE)!=-1) { // if not n<1
      if (breakSignal) return ONE;
      result=result.multiply(n);
      n=n.subtract(ONE);
    }

// at this point we have n less than 1, and result.  The argument to
// return will be gamma(1+n)*result.  If n=0 then gamma(1)=1, so:

    if (n.signum()==0) return result;

// Therefore, now we must find out what gamma(1+n) is.  This will not
// be ultraprecise no matter what we do, so we might as well use doubles.
// The code below computes the natural log of the gamma function using the
// Lanczos approximation formula.  The gamma function is defined by
//                                ( z - 1 )   -t
//        gamma( z ) = Integral[ t           e    dt ]
// integrated over the positive real domain.
// The gamma function satisfies gamma( n + 1 ) = n!
// This algorithm has been adapted from "Numerical Recipes", p. 157.

    double x = n.doubleValue();
    x=x+1;  // because it's gamma(x+1) we're getting now
    double coeff0 =  76.1800917300d;
    double coeff1 = -86.5053203300d;
    double coeff2 =  24.0140982200d;
    double coeff3 = -1.23173951600d;
    double coeff4 =  1.20858003000E-3d;
    double coeff5 = -5.36382000000E-6d;
    double stp    =  2.50662827465d;
    double r = (coeff0/x)+(coeff1/(x+1.0d))+(coeff2/(x+2.0d))
              +(coeff3/(x+3.0d))+(coeff4/(x+4.0d))+(coeff5/(x+5.0d));
    double s = x+4.5d;
    double t = (x-0.5d)*Math.log(s) - s;
    t = Math.exp(t);
    t *= (stp*(r+1.0d));
    double res=1.0d;
    try {
      res=result.doubleValue();
      res *= t;
      result = new BigDecimal(res);
    }
    catch (NumberFormatException exc)
    { result = result.multiply(new BigDecimal(t));
      return result.setScale(0,RHU); }
    return result.setScale(10,RHU);
  }

//--------------------------------------------------------------------
  private BigDecimal pi() {
    if (digits<=1000) {
      return (PI.setScale(digits,RHU));
    } else {
      int extradigits = 3;
      digits += extradigits;
      BigDecimal smallest = new BigDecimal(UNITY,digits); // 0.000...001
      BigDecimal a = ONE;
      BigDecimal x = ONE;
      BigDecimal b = ONE;
      b = b.divide(sqrt(TWO),digits,RHU);
      BigDecimal c = QUARTER;
      BigDecimal y = ZERO;
      BigDecimal pi_old = ZERO;
      BigDecimal pi_new = ONE;
      BigDecimal diff = pi_new.subtract(pi_old);
      while (diff.compareTo(smallest)==1) {
        if (breakSignal) return ONE;
        pi_old = pi_new;
        y = a;
        a = a.add(b);
        a = a.divide(TWO,digits,RHU); // a=(a+b)/2
        b = sqrt(b.multiply(y));      // b=sqrt(b*y)
        pi_new = a.subtract(y);  //pi_new is temporarily holding a value
        c = c.subtract(x.multiply(pi_new.multiply(pi_new)));  //c=c-x*(a-y)^2
        x = x.add(x);  // i.e. x=2*x
        pi_new = a.add(b);       //pi_new is temporarily holding again
        pi_new = pi_new.multiply(pi_new);
        y = c.multiply(new BigDecimal(4.0d)); //y is temporarily holding
        pi_new = pi_new.divide(y,digits,RHU);
                     // pi = (a+b)^2 / (4*c)
        diff = pi_new.subtract(pi_old);
        c=c.setScale(digits,RHU);
        x=x.setScale(digits,RHU);
        y=y.setScale(digits,RHU);
      }
      digits -= extradigits;
      pi_new = pi_new.setScale(digits,RHU);
      return pi_new;
    }
  }
//--------------------------------------------------------------------
  private BigDecimal sin (BigDecimal x){
    // ensure we're using radians
    if (degrees) {
      x = x.multiply(pi());
      x = x.divide(ONE_EIGHTY,digits,RHU);
    }
    // renormalize to the interval from -pi/2 to pi/2
    BigDecimal twopi = pi().multiply(TWO);
    x = x.divide(twopi,digits,RHU);
    x = x.subtract(x.setScale(0,BigDecimal.ROUND_DOWN));
               //This is the only way to get a fractional part!
    // interval (-1,1).
    if (x.compareTo(QUARTER.negate())==-1) {
      x = x.add(ONE);
    }
    // interval [-0.25,1).
    if (x.compareTo(new BigDecimal(0.75d))!=-1) {
      x = x.subtract(ONE);
    }
    // interval [-0.25,0.75).
    if (x.compareTo(QUARTER)==1) {
      x = HALF.subtract(x);
    }
    // interval [-0.25,0.25].
    x = x.multiply(twopi);
    // interval [-pi/2,pi/2].  What a pain in the ass...

    BigDecimal x2 = x.multiply(x);
    x2 = x2.setScale(digits,RHU);

    BigDecimal smallest = new BigDecimal(UNITY,digits); // 0.000...001
    BigDecimal n = ONE;
    BigDecimal term = x;
    BigDecimal acc_new = x;
    BigDecimal acc_old = ZERO;
    BigDecimal diff = acc_new.subtract(acc_old);
    diff = diff.abs();
    while (diff.compareTo(smallest)==1) {
      if (breakSignal) return ONE;
      acc_old = acc_new;
      term = term.multiply(x2);
      n = n.add(ONE);
      term = term.divide(n,digits,RHU);
      n = n.add(ONE);
      term = term.divide(n,digits,RHU);
      term = term.negate();
      acc_new = acc_new.add(term);
      diff = term.abs();
    }
    return acc_new.setScale(digits,RHU);
  }
//--------------------------------------------------------------------
  private BigDecimal cos (BigDecimal x){
    // could have called sin(pi/2 - x) or something but decided to
    // compute cosine directly- it's just too easy to cut and paste
    // and make minor changes.

    // ensure we're using radians
    if (degrees) {
      x = x.multiply(pi());
      x = x.divide(ONE_EIGHTY,digits,RHU);
    }
    // renormalize to the interval from 0 to pi
    BigDecimal twopi = pi().multiply(TWO);
    x = x.divide(twopi,digits,RHU);
    x = x.subtract(x.setScale(0,BigDecimal.ROUND_DOWN));

    // interval (-1,1).
    if (x.signum()==-1) {
      x = x.negate();
    }
    // interval [0,1).
    if (x.compareTo(HALF)!=-1) {
      x = ONE.subtract(x);
    }
    // interval [0,0.5).
    x = x.multiply(twopi);
    // interval [0,pi].

    BigDecimal x2 = x.multiply(x);
    x2 = x2.setScale(digits,RHU);

    BigDecimal smallest = new BigDecimal(UNITY,digits); // 0.000...001
    BigDecimal n = ZERO;
    boolean signIsPositive = false;
    BigDecimal term = ONE;
    BigDecimal acc_new = ONE;
    BigDecimal acc_old = ZERO;
    BigDecimal diff = term.abs();
    while (diff.compareTo(smallest)==1) {
      if (breakSignal) return ONE;
      acc_old = acc_new;
      term = term.multiply(x2);
      n = n.add(ONE);
      term = term.divide(n,digits,RHU);
      n = n.add(ONE);
      term = term.divide(n,digits,RHU);
      term = term.negate();
      acc_new = acc_new.add(term);
      diff = term.abs();
    }
    return acc_new.setScale(digits,RHU);
  }
//--------------------------------------------------------------------
  private BigDecimal tan (BigDecimal x){
    BigDecimal s=sin(x);
    BigDecimal c=sqrt(ONE.subtract(s.multiply(s)));
    if (breakSignal) return ONE;
    // Must determine the SIGN of the cos.
    if (degrees) {
      x = x.multiply(pi());
      x = x.divide(ONE_EIGHTY,digits,RHU);
    }
    BigDecimal twopi = pi().multiply(TWO);
    x = x.divide(twopi,digits,RHU);
    x = x.subtract(x.setScale(0,BigDecimal.ROUND_DOWN));
    if (x.signum()==-1) {
      x = x.negate();
    }
    if (x.compareTo(HALF)!=-1) {
      x = ONE.subtract(x);
    }
    if (x.compareTo(QUARTER)==1) {
      c=c.negate();
    }
    if (c.signum()==0) return ZERO;
    return s.divide(c,digits,RHU);
  }
//--------------------------------------------------------------------
  private BigDecimal arcsin (BigDecimal x){
    if (breakSignal) return ONE;
    BigDecimal x2 = x.multiply(x);
    x2 = x2.setScale(digits,RHU);
    if(x2.compareTo(ONE)==1) return ZERO; //arcsin domain is [-1,1]

    if(x2.compareTo(HALF)==1) {
      // Angle is greater than 45 degrees. Smaller angles work better.
      // Use identity arcsin(x)=pi/2 - arcsin(sqrt(1-x^2))
      BigDecimal temp;
      if (degrees)
        { temp = new BigDecimal(90.0d); }
      else
        { temp = pi().divide(TWO,digits,RHU); }

      temp = temp.subtract(arcsin(sqrt(ONE.subtract(x2))));

      if (x.signum()==-1)
        {return temp.negate();}
      else
        {return temp;}
    }

    BigDecimal smallest = new BigDecimal(UNITY,digits); // 0.000...001
    BigDecimal n = ONE;
    BigDecimal term;
    BigDecimal powr = x;
    BigDecimal acc_new = x;
    BigDecimal acc_old = ZERO;
    BigDecimal diff = acc_new.subtract(acc_old);
    BigDecimal oddsOverEvens= ONE;
    diff = diff.abs();
    while (diff.compareTo(smallest)==1) {
      if (breakSignal) return ONE;
      acc_old = acc_new;
      powr = powr.multiply(x2);
      powr = powr.setScale(digits,RHU);
      oddsOverEvens=oddsOverEvens.multiply(n);
      n = n.add(ONE);
      oddsOverEvens=oddsOverEvens.divide(n,digits,RHU);
      n = n.add(ONE);
      term = powr.multiply(oddsOverEvens);
      term = term.divide(n,digits,RHU);
      acc_new = acc_new.add(term);
      diff = term.abs();
    }

    if (degrees) {
      acc_new = acc_new.multiply(ONE_EIGHTY);
      acc_new = acc_new.divide(pi(),digits,RHU);
    }
    return acc_new.setScale(digits,RHU);
  }
//--------------------------------------------------------------------
  private BigDecimal arccos (BigDecimal x){
    BigDecimal x2 = x.multiply(x);
    x2 = x2.setScale(digits,RHU);
    if(x2.compareTo(ONE)==1) return ZERO; //arccos domain is [-1,1]

    if (x.signum()==-1)
      {
        BigDecimal temp;
        if (degrees) {temp=ONE_EIGHTY;}
        else {temp=pi();}
        return temp.subtract(arcsin(sqrt(ONE.subtract(x2))));
      }

    return arcsin(sqrt(ONE.subtract(x2)));
    }
//--------------------------------------------------------------------
  private BigDecimal arctan (BigDecimal x){
    BigDecimal x2 = x.multiply(x);
    x2 = x2.setScale(digits,RHU);
    return arcsin(x.divide(sqrt(x2.add(ONE)),digits,RHU));
  }
//--------------------------------------------------------------------
  private static BigDecimal set_PI() {
    // Defines the PI constant to be the 1000 digit approximation of pi.
    // If precision requires 1000 digits or fewer, the applet can use PI.
    String piS="3."
    + "14159265358979323846264338327950288419716939937510"
    + "58209749445923078164062862089986280348253421170679"
    + "82148086513282306647093844609550582231725359408128"
    + "48111745028410270193852110555964462294895493038196"
    + "44288109756659334461284756482337867831652712019091"
    + "45648566923460348610454326648213393607260249141273"
    + "72458700660631558817488152092096282925409171536436"
    + "78925903600113305305488204665213841469519415116094"
    + "33057270365759591953092186117381932611793105118548"
    + "07446237996274956735188575272489122793818301194912"
    + "98336733624406566430860213949463952247371907021798"
    + "60943702770539217176293176752384674818467669405132"
    + "00056812714526356082778577134275778960917363717872"
    + "14684409012249534301465495853710507922796892589235"
    + "42019956112129021960864034418159813629774771309960"
    + "51870721134999999837297804995105973173281609631859"
    + "50244594553469083026425223082533446850352619311881"
    + "71010003137838752886587533208381420617177669147303"
    + "59825349042875546873115956286388235378759375195778"
    + "18577805321712268066130019278766111959092164201989";

    return new BigDecimal(piS);
  }
//--------------------------------------------------------------------
  private static BigDecimal set_LN10() {
    // orignal coding started by www.geocities.com/SiliconValley/2548/BigCalculator.java
    // Defines the LN10 constant to be the 1000 digit approximation of
    // the natural log of ten. If precision requires 1000 digits or
    // fewer, the applet can use LN10 when computing common logarithms.
    String ln10S="2."
    + "30258509299404568401799145468436420760110148862877"
    + "29760333279009675726096773524802359972050895982983"
    + "41967784042286248633409525465082806756666287369098"
    + "78168948290720832555468084379989482623319852839350"
    + "53089653777326288461633662222876982198867465436674"
    + "74404243274365155048934314939391479619404400222105"
    + "10171417480036880840126470806855677432162283552201"
    + "14804663715659121373450747856947683463616792101806"
    + "44507064800027750268491674655058685693567342067058"
    + "11364292245544057589257242082413146956890167589402"
    + "56776311356919292033376587141660230105703089634572"
    + "07544037084746994016826928280848118428931484852494"
    + "86448719278096762712757753970276686059524967166741"
    + "83485704422507197965004714951050492214776567636938"
    + "66297697952211071826454973477266242570942932258279"
    + "85025855097852653832076067263171643095059950878075"
    + "23710333101197857547331541421808427543863591778117"
    + "05430982748238504564801909561029929182431823752535"
    + "77097505395651876975103749708886921802051893395072"
    + "38539205144634197265287286965110862571492198849979";

    return new BigDecimal(ln10S);
  }

}
Hosted by www.Geocities.ws

1