/*
 * numerical integration example, as discussed in textbook:
 *
 * compute pi by approximating the area under the curve f(x) = 4 / (1 + x*x)
 * between 0 and 1.
 *
 * command-line argument specifies number of threads.
 * simple parallel version.
 */
package csci3366.sample.numint;
import csci3366.sample.utility.Utility;

public class NumIntPar1 {

    /* variables to be used by all threads -- ugly but simple */

    private static long numSteps;
    private static double step;
    private static double sum = 0.0;
    private static int numThreads;

    /* main method */

    public static void main(String[] args) {

        /* process command-line arguments */

        String usageMessage = "arguments:  number_of_steps number_of_threads";

        numSteps = Utility.getLongArg(args, 0, 1, 
                "number_of_steps", usageMessage);
        numThreads = Utility.getIntegerArg(args, 1, 1, 
                "number_of_threads", usageMessage);

        step = 1.0/(double) numSteps; 

        /* start timing */
        long startTime = System.currentTimeMillis();

        /* create threads */
        Thread[] threads = new Thread[numThreads];
        for (int i = 0; i < threads.length; ++i) {
            threads[i] = new Thread(new CodeForThread(i));
        }

        /* start them up */
        for (int i = 0; i < threads.length; ++i) {
            threads[i].start();
        }

        /* wait for them to finish */
        for (int i = 0; i < threads.length; ++i) {
            try {
                threads[i].join();
            }
            catch (InterruptedException e) {
                System.err.println("should not happen");
            }
        }

        /* finish computation */
        double pi = sum * step;

        /* end timing and print result */
        long endTime = System.currentTimeMillis();
        System.out.printf(
                "parallel program (v1) results with %d threads and %d steps\n",
                numThreads, numSteps);
        System.out.printf("computed pi = %17.15f\n" , pi);
        System.out.printf(
                "difference between estimated pi and Math.PI = %17.15f\n",
                Math.abs(pi - Math.PI));
        System.out.printf("time to compute = %g seconds\n",
                (double) (endTime - startTime) / 1000);
    }

    /* static inner class to contain code to run in each thread */

    private static class CodeForThread implements Runnable {

        private int myID;

        public CodeForThread(int myID_) {
            myID = myID;
        }

        public void run() {
            double partsum = 0.0;
            for (int i=myID; i < numSteps; i += numThreads) {
                double x = (i+0.5)*step;
                partsum += 4.0/(1.0+x*x);
            }
            /* only one thread at a time can do this */
            synchronized(getClass()) {
                sum += partsum;
            }
        }
    }
}