
// CalcPiSeq.java
// Andrew Davison, ad@coe.psu.ac.th, May 2023

/* Pi can be calculated using the integral of f(x) = 4/(1+x*x) 
   from O to 1. This can be implemented 
   by summing the area under f(x) between 0 and 1
   by incrementing x in very small steps.
   BigDecimals are used to avoid rounding errors.

   The number of steps goes from 0 to NUM_STEPS, but
   is divided into NUM_RANGES step ranges. Each of these
   ranges are passed to an areaRange() function which
   calculates the area for that range. These areas are added
   together to get the total, and printed.

  Inspired by Session #3 of the slides:
        Tutorial on Parallel Programming with Linda
        Scientific Computing Associates, 2006
  at
https://www.cse.chalmers.se/edu/year/2019/course/TDA384_LP1/files/lectures/semantic-linda-biglecture.pdf

  Usage:
    > javac CalcPiSeq.java
    > java CalcPiSeq

  Execution:
    > java CalcPiSeq
    No. of steps: 100000000
    No. ranges: 4
    Steps range: 0 --> 25000000
    Steps range: 25000000 --> 50000000
    Steps range: 50000000 --> 75000000
    Steps range: 75000000 --> 100000000
    Time taken:  38183ms
    Result:  3.141592663589793221842080000000
    Math.PI: 3.141592653589793

  The concurrent tuple version takes around 19 secs.

  Other version: 
    * CalcPiSpace.java
*/

import java.util.*;
import java.math.*;


public class CalcPiSeq
{
  private static final int NUM_STEPS = 100000000;
  private static final int NUM_RANGES = 4;


  public static void main(String[] args)
  {
    long startTime = System.currentTimeMillis();

    System.out.println("No. of steps: " + NUM_STEPS);
    System.out.println("No. ranges: " + NUM_RANGES);

    BigDecimal xStep = (BigDecimal.ONE).divide(
            BigDecimal.valueOf(NUM_STEPS), 15, RoundingMode.HALF_UP);
           // x-step is 1/(no. of steps) (between 0 and 1)

    BigDecimal[] areas = new BigDecimal[NUM_RANGES];
    int interval = NUM_STEPS/NUM_RANGES;
       /* divide the number of steps into NUM_RANGES
          intervals; this will make more sense when the code is
          converted to use threads */
    int startStep = 0;
    int endStep = interval;

    // call areaRange() NUM_RANGES times, 
    // storing the answers in areas[]
    for (int i=0; i < NUM_RANGES; i++) {
       areas[i] = areaRange(startStep, endStep, xStep);
       startStep = endStep;   // move to next interval
       endStep += interval;
    }

    // sum the area values
    BigDecimal area = BigDecimal.ZERO;
    for (int i=0; i < NUM_RANGES; i++)
       area = area.add(areas[i]);

    long elapsedTime = System.currentTimeMillis() - startTime;
    System.out.println("Time taken:  " + elapsedTime + "ms");
    System.out.println("Result:  " +  area);
    System.out.println("Math.PI: " + Math.PI);  // for comparison
  } /* main */



  private static BigDecimal areaRange(int startStep, int endStep, 
                                      BigDecimal xStep)
  /* The calculation range is supplied in terms of the
     number of steps in this range. startStep is converted
     into a BigDecimal x value before the function is called,
     and then incremented using xStep.
  */
  {
    System.out.println("Steps range: " + startStep + 
                               " --> " + endStep);

    BigDecimal four = BigDecimal.valueOf(4);
    BigDecimal result = BigDecimal.ZERO; 
    BigDecimal x = BigDecimal.valueOf(startStep).multiply(xStep);
          // starting x value == startStep*xStep
    BigDecimal divisor;

    for(int i = startStep; i < endStep; i++) { 
      divisor = (BigDecimal.ONE).add(x.multiply(x));  // (1.0+x*x)
      result = result.add(
           four.divide(divisor, 15, RoundingMode.HALF_UP));
           // result += 4.0/(1.0+x*x); 
      x = x.add(xStep); // next x value
    }

    // return the area under the curve
    return result.multiply(xStep);
  }  // end of areaRange()


}  // end of CalcPiSeq class
