
// CalcPiSpace.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, which are
   stored in the space as tuples of the form:
      ("RANGE", startStep, endStep) 

   NUM_RANGES PiWorker threads are created, each of which 
   reads one RANGE tuple
   and converts the steps into x values using the xStep value
   stored in the ("XSTEP", xStep) tuple. 

   Each PiWorker uses the same areaRange() function as in the
   sequential version of this problem in CalcPiSeq.java, but the
   subarea answer is stored in the space as a tuple of the form:
     ("AREA", <worker id>, area)

   CalcPiSpace waits for "AREA" tuples, and adds the subareas
   together. When it has seen NUM_RANGES of these, it prints 
   the total.

   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:
    > compile.bat CalcPiSpace.java
    > run.bat CalcPiSpace

  Execution:
    > run CalcPiSpace

    No. of steps: 100000000
    No. ranges: 4
    Storing xStep in space
    Placing 4 ranges in space
    Creating 4 workers
    Steps range for W-1: 75000000 --> 100000000
    Steps range for W-3: 25000000 --> 50000000
    Steps range for W-0: 0 --> 25000000
    Steps range for W-2: 50000000 --> 75000000
    Got subarea from W-2
    Got subarea from W-1
    Got subarea from W-3
    Got subarea from W-0

    Time taken:  18863ms
    Result:  3.141592663589793221842080000000
    Math.PI: 3.141592653589793

    The sequential version takes about 38 secs.

    Part of the application made up of:
       * CalcPiSpace.java
       * PiWorker.java

  Other version: 
    * CalcPiSeq.java
*/

import java.util.*;
import java.math.*;

import org.jspace.*;


public class CalcPiSpace
{
  private static final int NUM_STEPS = 100000000;
  private static final int NUM_RANGES = 4;


  public static void main(String[] args) 
                            throws InterruptedException
  {
    Space space = new SequentialSpace();
    // Space space = new VisSpace(100);   // smaller DELAY

    System.out.println("No. of steps: " + NUM_STEPS);
    System.out.println("No. ranges: " + NUM_RANGES);

    long startTime = System.currentTimeMillis();

    BigDecimal xStep = (BigDecimal.ONE).divide(
            BigDecimal.valueOf(NUM_STEPS), 15, 
                               RoundingMode.HALF_UP);
        // x-step is 1/(no. of steps) (between 0 and 1)

    System.out.println("Storing xStep in space");
    space.put("XSTEP", xStep);

    BigDecimal[] areas = new BigDecimal[NUM_RANGES];
    int interval = NUM_STEPS/NUM_RANGES;
    int startStep = 0;
    int endStep = interval;

    System.out.println("Placing " + NUM_RANGES + 
                                   " ranges in space");
    for (int i=0; i < NUM_RANGES; i++) {
       space.put("RANGE", startStep, endStep);
       startStep = endStep;   // move to next step interval
       endStep += interval;
    }

    System.out.println("Creating " + NUM_RANGES + " workers");
    for (int i=0; i < NUM_RANGES; i++)
      new Thread( new PiWorker(i, space), "W-"+i).start();

    // wait for the subarea calculation results (in any order)
    BigDecimal area = BigDecimal.ZERO;
    for (int i=0; i < NUM_RANGES; i++) {
      Object[] resObj = space.get(
               new ActualField("AREA"), 
               new FormalField(Integer.class),      // worker id
               new FormalField(BigDecimal.class));  // subarea
      int id = (int)resObj[1];
      BigDecimal subArea =  (BigDecimal)resObj[2];
      System.out.println("Got subarea from W-" + id);
      area = area.add(subArea);   // sum the areas
    }

    long elapsedTime = System.currentTimeMillis() - startTime;
    System.out.println("\nTime taken:  " + elapsedTime + "ms");
    System.out.println("Result:  " +  area);
    System.out.println("Math.PI: " + Math.PI);  // for comparison
  } /* main */

} // end of CalcPiSpace class
