/*
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

import java.util.*;
import java.util.concurrent.*;

public class FJPi {

    public static void main(String[] args) {
        long n = 1_000_000_000L;
        int maxPar = Runtime.getRuntime().availableProcessors();
        if (args.length > 0)
            n = Long.parseLong(args[0]);
        if (args.length > 1)
            maxPar = Integer.parseInt(args[1]);

        for (int reps = 0; reps < 3; ++reps) {
            long startTime = System.nanoTime();
            long count = seq(n);
            double time = (System.nanoTime() - startTime) / 1.0e9;
            double pi = count * 4.0 / n;
            System.out.println("SR  pi: " + pi + " time: " + time + "s");
        }
        
        for (int reps = 0; reps < 3; ++reps) {
            long startTime = System.nanoTime();
            long count = jurseq(n);
            double time = (System.nanoTime() - startTime) / 1.0e9;
            double pi = count * 4.0 / n;
            System.out.println("JUR pi: " + pi + " time: " + time + "s");
        }

        for (int p = 1; p <= maxPar; ++p) {
            System.out.println("trials: " + n + " parallelism: " + p);
            long threshold = Math.max(1000, n / (8 * p));
            ForkJoinPool fjp = new ForkJoinPool(p);

            SplittableRandom sr = new SplittableRandom();
            for (int reps = 0; reps < 3; ++reps) {
                long startTime = System.nanoTime();
                long count = fjp.invoke(new PiTask(sr, n, threshold));
                double time = (System.nanoTime() - startTime) / 1.0e9;
                double pi = count * 4.0 / n;
                System.out.println("SR  pi: " + pi + " time: " + time + "s");
            }

            Random rnd = new Random();
            for (int reps = 0; reps < 3; ++reps) {
                long startTime = System.nanoTime();
                long count = fjp.invoke(new JURPiTask(rnd, n, threshold));
                double time = (System.nanoTime() - startTime) / 1.0e9;
                double pi = count * 4.0 / n;
                System.out.println("JUR pi: " + pi + " time: " + time + "s");
            }

            fjp.shutdownNow();
            // reduce measurement noise
            fjp = null; sr = null; rnd = null;  System.gc();
        }

        for (int reps = 0; reps < 3; ++reps) {
            long startTime = System.nanoTime();
            long count = seq(n);
            double time = (System.nanoTime() - startTime) / 1.0e9;
            double pi = count * 4.0 / n;
            System.out.println("SR  pi: " + pi + " time: " + time + "s");
        }
        
        for (int reps = 0; reps < 3; ++reps) {
            long startTime = System.nanoTime();
            long count = jurseq(n);
            double time = (System.nanoTime() - startTime) / 1.0e9;
            double pi = count * 4.0 / n;
            System.out.println("JUR pi: " + pi + " time: " + time + "s");
        }

    }

    static long seq(long n) {
        SplittableRandom sr = new SplittableRandom();
        long count = 0;
        for (long i = n; i > 0; --i) {
            double x = sr.nextDouble(), y = sr.nextDouble();
            if (x * x + y * y < 1.0)
                ++count;
        }
        return count;
    }

    static long jurseq(long n) {
        Random rnd = new Random();
        long count = 0;
        for (long i = n; i > 0; --i) {
            double x = rnd.nextDouble(), y = rnd.nextDouble();
            if (x * x + y * y < 1.0)
                ++count;
        }
        return count;
    }

    static final class PiTask extends RecursiveTask<Long> {
        final SplittableRandom sr; final long n, threshold; 
        PiTask(SplittableRandom r, long s, long t) { 
            sr = r; n = s; threshold = t; 
        }
        public Long compute() {
            if (n > threshold) {
                long h = n >>> 1;
                PiTask p = new PiTask(sr.split(), h, threshold);
                p.fork();
                return new PiTask(sr, n - h, threshold).compute() + p.join();
            }
            else {
                long count = 0;
                for (long i = n; i > 0; --i) {
                    double x = sr.nextDouble(), y = sr.nextDouble();
                    if (x * x + y * y < 1.0)
                        ++count;
                }
                return count;
            }
        }
    }

    static final class JURPiTask extends RecursiveTask<Long> {
        final Random rnd; final long n, threshold; 
        JURPiTask(Random r, long s, long t) { 
            rnd = r; n = s; threshold = t; 
        }
        public Long compute() {
            if (n > threshold) {
                long h = n >>> 1;
                JURPiTask p = new JURPiTask(rnd, h, threshold);
                p.fork();
                return new JURPiTask(rnd, n - h, threshold).compute() + p.join();
            }
            else {
                long count = 0;
                for (long i = n; i > 0; --i) {
                    double x = rnd.nextDouble(), y = rnd.nextDouble();
                    if (x * x + y * y < 1.0)
                        ++count;
                }
                return count;
            }
        }
    }

}