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

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

/**
 * BinaryTrees.java --- Parallel example of binary tree functions using futures
 *
 * @author Vivek Sarkar (vsarkar@rice.edu)
 *
 * This example program a binary tree of minimum depth 4, and checks the correctness of the tree.
 * The default value for the depth is 0, 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 BinaryTrees {

    private final static int minDepth = 4;

    /**
     * Main function. Can take one input parameter,
     * which is the depth of the tree. If the depth is
     * not given or is less then 4, use 4, the minimum
     * depth
     * @param args input parameters
     */
    public static void main(String[] args){

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

        int n = 0;
        if (args.length > 0) n = Integer.parseInt(args[0]);

        int maxDepth = (minDepth + 2 > n) ? minDepth + 2 : n;
        int stretchDepth = maxDepth + 1;

        int check = (TreeNode.bottomUpTree(0,stretchDepth)).itemCheck();
        System.out.println("stretch tree of depth "+stretchDepth+"\t check: " + check);

        TreeNode longLivedTree = TreeNode.bottomUpTree(0,maxDepth);

        for (int depth=minDepth; depth<=maxDepth; depth+=2){
            int iterations = 1 << (maxDepth - depth + minDepth);
            check = 0;

            for (int i=1; i<=iterations; i++){
                check += (TreeNode.bottomUpTree(i,depth)).itemCheck();
                check += (TreeNode.bottomUpTree(-i,depth)).itemCheck();
            }
            System.out.println((iterations*2) + "\t trees of depth " + depth + "\t check: " + check);
        }
        System.out.println("long lived tree of depth " + maxDepth + "\t check: "+ longLivedTree.itemCheck());

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


    /**
     * Object defining the node of a binary tree. The
     * tree itself is built as a series of nodes
     * hooked up together.
     */
    private static class TreeNode
    {
        /**
         * Left child
         */
        final private TreeNode left;

        /**
         * Right child
         */
        final private TreeNode right;

        /**
         * Value at this node
         */
        private int item;

        /**
         * Constructor for a leaf node
         * @param item - the value stored at this node
         */
        TreeNode(int item){
            this.item = item;
            this.left = null;
            this.right = null;
        }

        /**
         * Alternate constructor for a node, constructs a non-lear node
         * @param left - the left child, a TreeNode
         * @param right - the right child, a TreeNode
         * @param item - the value stored at this node
         */
        TreeNode(TreeNode left, TreeNode right, int item){
            this.left = left;
            this.right = right;
            this.item = item;
        }

        /**
         * Build a tree from the bottom up
         * @param item The value of the item to use in this node
         * @param depth the currect depth we are at (goes downwards)
         * @return a new TreeNode
         */
        private static TreeNode bottomUpTree(int item, int depth){

            doWork(1);
            if (depth>0){
                final TreeNode LNode = bottomUpTree(2*item-1, depth-1);
                final TreeNode RNode = bottomUpTree(2*item, depth-1);
                return new TreeNode(LNode, RNode, item);
            }
            else {
                return new TreeNode(item);
            }
        }

        /**
         * Returns the value of the node for leaves, or the value of
         * the node plus the value of the left child minus the value
         * of the right child, checks that the tree was
         * built correctly
         * @return - the check value of the node
         */
        private int itemCheck(){
            // if necessary deallocate here
            if (left==null) return item;
            else return item + left.itemCheck() - right.itemCheck();
        }
    }

}