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

class ParallelMonkeys {
    public enum RunType {
        Sequential, ParallelNaive, ParallelRange;
    }
    
    public static void main(String[] args) {
        ParallelMonkeys pm = new ParallelMonkeys();
        pm.compute(RunType.Sequential);
        pm.compute(RunType.ParallelNaive);
        pm.compute(RunType.ParallelRange);
    }
    
    private int size = 1000000;
    
    private void compute(RunType runType) {
        System.err.format("%n... runType:%s %n", runType.toString());
        long startTime = System.currentTimeMillis();

        if (runType == RunType.Sequential) {
            for (int i = 0; i < size; i++) {
                int k = task(i);
            }
            
        } else {        
            List<Callable<Void>> callables = new LinkedList<Callable<Void>>();

            if (runType == RunType.ParallelNaive) {
                for (int i = 0; i < size; i++) {
                    final int ii = i;
                    callables.add(new Callable<Void>() {
                        public Void call() {
                            int k = task(ii);
                            return null;
                        }
                    });
                }
                
            } else { // runType == RunType.ParallelRange
                int chunk = 500;
                
                for (int low = 0; low < size; low += chunk) {
                    final int Low = low;
                    final int High = Math.min(size, low+chunk);
                    callables.add(new Callable<Void>() {                        
                        public Void call() {
                            for (int i = Low; i < High; i++) {
                                int k = task(i);
                            }
                            return null;
                        }
                    });
                }
            }

            int maxallowed_threads = 1 * Runtime.getRuntime().availableProcessors();
            ExecutorService executor = new ForkJoinPool(maxallowed_threads); 
                                        // Executors.newFixedThreadPool(maxallowed_threads);
            
            try {
                List<Future<Void>> futures = executor.invokeAll(callables);
            } catch (Exception ex) {
                System.err.format ("*** %s %n", ex.getMessage());
            }
            
        }
        
        long endTime = System.currentTimeMillis();
        System.err.format("    runTime:%.3f %n", (endTime-startTime) / 1000.0f);
    }
    
    private int task(int i) {
        int spin = 500;
        int k = 0;
        for (int j=0; j<spin; j++) {
            int m = 0;
            for (int r=0; r<spin; r++) {
                m += i;
            }             // m == spin * i
            k += m/spin; 
        }                 // k == spin * i
        return k/spin;    // return i
    }
}

        