import java.util.Random;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.Callable;
import java.time.Instant;
import java.time.Duration;
import java.util.Arrays;

class Example {
    static final int SIZE = 100000000;
    static final int PARTS = 4;
    static final int PART_SZ = SIZE/PARTS;
    static final int SEQUENTIAL_CUTOFF = 1000;

    static <T> void timedRun(Callable<T> function) throws Exception {
        Instant start = Instant.now();
        System.out.println("Result: " + function.call() +
            " Time: " + Duration.between(start, Instant.now()).toMillis() + "ms");
    }

    static int compute(int[] arr, int lo, int hi) {
        if (hi-lo < SEQUENTIAL_CUTOFF) {
            int ans = 0;
            for (int i=lo; i<hi; ++i) {
                ans += arr[i];
            }
            return ans;
        } else {
            ForkJoinTask<Integer> left =
                ForkJoinTask.adapt(()->compute(arr, lo, (hi+lo)/2)).fork();
            int rightAns = compute(arr, (hi+lo)/2, hi);
            int leftAns = left.join();
            return leftAns + rightAns;
        }
    }

    public static void main(String[] args) throws Exception {
      int[] data = new Random().ints(SIZE).toArray();

      timedRun(()->{
        int finalSum = 0;
        for (int x : data) {
            finalSum += x;
        }
        return finalSum;
      });

      timedRun(()-> {
        Thread[] threadList = new Thread[PARTS];
        int[] threadSum = new int[PARTS];
        for (int i=0; i<PARTS; ++i) {
            final int tid = i;
            threadSum[i] = 0;
            threadList[i] = new Thread(() -> {
                for (int j=tid*PART_SZ; j<(tid+1)*PART_SZ; ++j) {
                    threadSum[tid] += data[j];
                }
            });
            threadList[i].start();
        }
        int finalSum = 0;
        for (int i=0; i<PARTS; ++i) {
            threadList[i].join();
            finalSum += threadSum[i];
        }
        return finalSum;
      });

      timedRun(()->compute(data, 0, SIZE));
      timedRun(()->compute(data, 0, SIZE));

      timedRun(()->Arrays.stream(data).parallel().sum());
      timedRun(()->Arrays.stream(data).parallel().sum());
    }
}