import edu.rice.hj.api.HjFuture;
import edu.rice.hj.api.HjMetrics;
import edu.rice.hj.runtime.config.HjSystemProperty;
import edu.rice.hj.runtime.metrics.AbstractMetricsManager;

import java.util.Random;

import static edu.rice.hj.Module1.*;

/**
 * ArraySum2.hj --- Parallel recursive example program for computing the sum of an array using futures.
 *
 * @author Vivek Sarkar (vsarkar@rice.edu)
 *
 * This example program creates an array of n random int's, and computes their sum in parallel.
 * The default value of n is 128, but any size can be provided as argv[0]
 *
 * NOTE: this example program is intended for illustrating abstract performance metrics,
 * and is not intended to be used as abenchmark for real performance.
 */
public class ArraySum2 {
    static final int default_n = 128;
    static final String err = "Incorrect argument for array size (should be > 0), assuming n = " + default_n;

    /**
     * Computes the sum of an array of integers between two indexes
     * @param X the array to sum up
     * @param lo The leftmost index to use in the sum
     * @param hi the rightmost index to use in the sum
     * @return - the sum of all elements in between the two indexes
     */
    static int computeSum(int[] X, int lo, int hi) {
        if ( lo > hi ) {
            return 0;
        } else if ( lo == hi ) {
            return X[lo];
        } else {
            int mid = (lo+hi)/2;
            final HjFuture<Integer> sum1 = future(() -> computeSum(X, lo, mid));
            final HjFuture<Integer> sum2 = future(() -> computeSum(X, mid+1, hi));
            int local_sum = sum1.get() + sum2.get();
            doWork(1); // Assume that add takes 1 unit of time, and that everything else is free
            return local_sum;
        }
    } // computeSum

    /**
     * Main function. Input can take one parameter, which is size
     * of the array. If there are no input parameters, use a default size
     * @param argv - the input parameter
     */
    public static void main(String[] argv) {

        // Setup metrics
        System.setProperty(HjSystemProperty.abstractMetrics.propertyKey(), "true");
        initializeHabanero();

        // Initialization
        int n;
        int[] X;
        if (argv.length != 0) {
            try {
                n = Integer.parseInt(argv[0]);
                if (n <= 0) {
                    // Bad value of n
                    System.out.println(err);
                    n = default_n;
                }
            } catch (Throwable e) {
                System.out.println(err);
                n = default_n;
            }
        }
        else { // argv.length == 0
            n = default_n;
        }
        X = new int[n];
        Random myRand = new Random(n);

        for (int i = 0; i < n; i++)
            X[i] = myRand.nextInt(n);

        // Recursive parallel computation
        int sum = computeSum(X, 0, n-1);

        // Output
        System.out.println("Sum of " + n + " elements = " + sum);

        // Print out the metrics data
        finalizeHabanero();
        final HjMetrics actualMetrics = abstractMetrics();
        AbstractMetricsManager.dumpStatistics(actualMetrics);
        System.out.println(actualMetrics);
    }
}