// Applet that computes pi
// Author Steve Merrin June 1998
// Computes pi to any desired accuracy; user can choose
// from three formulas:
// formula 1: pi = 16arctan(1/5) - 4arctan(1/239)
// formula 2: pi = 8arctan(1/3) + 4arctan(1/7)
// formula 3: pi = 4arctan(1/2) + 4arctan(1/3)
                   
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.math.BigInteger;

public class AppletPi3 extends Applet implements ActionListener, ItemListener
{
 final int COLS = 50;   // display pi in blocks ROWS by COLS
 final int ROWS = 10;
 TextField numDigits = new TextField(7);
 TextArea output =
   new TextArea( "", ROWS + 7, COLS + 2, TextArea.SCROLLBARS_VERTICAL_ONLY);
 int choice = 1;  // 3 formulas to choose from
 String[] formula = { "16arctan(1/5) - 4arctan(1/239)",
                      "8arctan(1/3) + 4arctan(1/7)",
                      "4arctan(1/2) + 4arctan(1/3)" };
 String initialText =
  "Choose the formula to be used to compute pi.\nThe first one is " +
  "the fastest:\n" +
  "formula 1 is pi = " + formula[0] + "\n" +
  "formula 2 is pi = " + formula[1] + "\n" +
  "formula 3 is pi = " + formula[2] + "\n\n" +
  "Enter the number of places in the box above and press return.";
 Button show = new Button( "show formulas" );
 // button puts initial prompting text back into the text area
 Panel p = new Panel();  // panel at bottom of window
 CheckboxGroup g = new CheckboxGroup();  // group 3 radio buttons
 Checkbox fmla1 = new Checkbox( "formula 1", g, true );
 Checkbox fmla2 = new Checkbox( "formula 2", g, false );
 Checkbox fmla3 = new Checkbox( "formula 3", g, false );
 Panel p1 = new Panel();  // panel for top of Window

 public void init()
 {
  setLayout(new BorderLayout());
  setBackground(Color.lightGray);
  numDigits.addActionListener(this);

  p1.setLayout( new FlowLayout() );
  p1.add( new Label("Author: Steve Merrin") );
  p1.add( new Label("Compute pi") );
  p1.add( new Label("Enter number of decimal places:", Label.RIGHT) );
  p1.add(numDigits);
  add(p1, "North");

  add(output, "Center");
  output.setText( initialText );

  p.setLayout( new FlowLayout() );
  p.add( new Label( "Choose formula:", Label.CENTER ) );
  p.add(fmla1);  p.add(fmla2);  p.add(fmla3);  // three radio buttons
  // Below, button show used to restore initial text in text area
  p.add(show);
  add(p, "South");  // add panel of 3 radio buttons plus show button
  show.addActionListener(this);
  fmla1.addItemListener(this);
  fmla2.addItemListener(this);
  fmla3.addItemListener(this);
 }

 public void itemStateChanged( ItemEvent e )  // handle radio buttons
 {
  if( e.getItem().equals("formula 1") ) {choice = 1; return;}
  if( e.getItem().equals("formula 2") ) {choice = 2; return;}
  if( e.getItem().equals("formula 3") ) {choice = 3; return;}
 }

 public void actionPerformed(ActionEvent e)
 {
// event could be clicking the "show" button or else pressing Enter
// with the cursor in the text field

  if( e.getActionCommand().equals("show formulas") )
  {
   // "show" button was clicked
   output.setText( initialText );
   return;
  }

  // Below, action to take when user presses Enter, with cursor in textfield
  int N = Integer.parseInt( numDigits.getText() );
  output.setText("Using formula " + choice + "\nComputation of pi to " + N +
                 " places now in progress ...");
  long time1 = System.currentTimeMillis();  // time how long it takes
  String piString = PiN3.piDigits(N, choice).toString();
  //  piString is 31415...
  long time2 = System.currentTimeMillis();
  output.setText( "3.\n" );
  int digitsRemaining = N;
  int next = 1;  // index into piString, initialized to point to 2nd char
  while( digitsRemaining > 0 )  // loop that extracts substrings from
  {                             // piString
   if( digitsRemaining >= COLS )
   {
    output.append( piString.substring(next, next + COLS) + "\n" );
    next += COLS;
    digitsRemaining -= COLS;
   }
   else
   {
     output.append( piString.substring(next,
                    next + digitsRemaining) + "\n" );
     digitsRemaining = 0;
   }

   if( (N - digitsRemaining) % (COLS * ROWS) == 0 )
    output.append( "(number of digits displayed so far: " +
                   (N - digitsRemaining) + ")\n" );
  } // while
  long deltaSeconds = (time2 - time1)/1000;  // elapsed time in seconds
  output.append( "pi computation time: " + deltaSeconds/60 + " min "
                 + deltaSeconds % 60 + " sec\n" );
  output.append( "total number of digits displayed: " + N + "\n" );
  output.append( "formula " + choice + " used: " + formula[choice - 1] );
 } // actionPerformed

} // AppletPi3


// author: Steve Merrin  June 98
// Computes pi to any desired accuracy; user can choose
// from three formulas (formula 1 is the fastest):
// formula 1: pi = 16arctan(1/5) - 4arctan(1/239)
// formula 2: pi = 8arctan(1/3) + 4arctan(1/7)
// formula 3: pi = 4arctan(1/2) + 4arctan(1/3)

class PiN3
{

  // Finds integer part of 10^N times arctan(1/k)
  public static BigInteger invtan( int k, int N )
  {
    int i;
    int a = k*k;
    int M = 0;
    while( (Math.log(2*M + 3) + (M + 1)*Math.log(a)) <= N*Math.log(10) )
     M++;
//  M is the degree of the Taylor polynomial needed to achieve N
//  digit accuracy of arctan(1/k).

    BigInteger tenToN = new BigInteger("1");
    //  The following loop computes 10^N
    for( i = 0; i < N; i++ )
      tenToN = tenToN.multiply(BigInteger.valueOf(10));

    int c = 2*M + 1;
    BigInteger s = tenToN.divide(BigInteger.valueOf(c)); // s = (10^N)/c
    for( i = 0; i < M; i++ )
    {
      c = c - 2;
      s = (tenToN.divide(BigInteger.valueOf(c))).
          subtract(s.divide(BigInteger.valueOf(a)));   //  s = (10^N)/c - s/a
    } 
    return s.divide(BigInteger.valueOf(k));
    // return s/k, which is integer part of 10^N times arctan(1/k)
  }


  // computes pi with N digits following the decimal point
  // choice == 1  use formula 1
  // choice == 2  use formula 2
  // choice == 3  use formula 3
  public static BigInteger piDigits(int N, int choice)
  {
    N += 8;
    //  To be safe, compute 8 extra digits, to be dropped at end
    //  The 8 is arbitrary
    BigInteger pi = new BigInteger("1");  // compiler requires init

    if( choice == 1 )
     pi = (invtan(5,N).multiply(BigInteger.valueOf(16))).subtract
          (invtan(239,N).multiply(BigInteger.valueOf(4)));

    if( choice == 2 )
     pi = (invtan(3,N).multiply(BigInteger.valueOf(8))).add
          (invtan(7,N).multiply(BigInteger.valueOf(4)));

    if( choice == 3 )
     pi = (invtan(2,N).multiply(BigInteger.valueOf(4))).add
          (invtan(3,N).multiply(BigInteger.valueOf(4)));

    return pi.divide(BigInteger.valueOf(100000000));  // drop last 8 digits
  } // piDigits

} // class PiN3